Ваш обозреватель не поддерживает возможности, необходимые для корректной работы impress.js, поэтому презентация будет показана в упрощенном виде.

Управление памятью в Rust

Куваев Александр, Rustacean

Жалко ресурсов на сборку мусора, но и не хочется заниматься ручным управлением памятью?

Хочется потокобезопасности без накладных расходов во время выполнения?

Тогда вам будет интересно узнать про язык программирования Rust

Разрабатывается Mozilla Foundation при поддержке сообщества

Разработка ведётся на GitHub

Что же такое Rust?

Это системный язык программирования, который предотвращает утечки памяти, обращение к освобождённым данным, ситуации гонок в многопоточных приложениях без накладных расходов во время выполнения.

Кроме того стоит заметить, что абстракции в Rust также имеют нулевую стоимость за счёт статической диспетчеризации во время компиляции.

Экосистема

Компилятор поставляется вместе с менеджером пактов cargo, который может устанавливать пакеты как из Git-репозиториев, так и из официального репозитория crates.io. В настоящий момент публикация на crates.io возможна только для пакетов с лицензией, позволяющей открытие исходного кода. Другими словами - публикация происходит в виде выкладывания исходников в общий доступ.

Сам исходный код компилятора rustc лицензирован по MIT и Apache 2.0 лицензиям.

Работа с памятью

Безопасные языки вроде Java, C#, Python, JavaScript используют сборщики мусора. Для сборщиков мусора требуется некая среда исполнения с наличием в ней сборщика мусора, кроме того сборщик отнимает процессорное время у приложения.

Небезопасные языки вроде C, C++, Delphi не создают дополнительных накладных расходов, однако весь менеджмент памятью остаётся полностью на разработчике, что зачастую выливается в увлекательные поиски трудноповторимого редкого бага.

Связывание имён

Любая программа на Rust использует связывание имён. Они связывают значение с именем, для того чтобы воспользоваться им позже. Для связывания используется ключевое слово let:

fn main() {
    let x = 5;
}

Далее будем считать, что все действия происходят внутри функции main

Шаблоны связывания

В левой части выражения let располагается не просто имя переменной, а «шаблон». Связываение происходит попарно имя-значение:

let (x, y) = (5, 6);

println!("x = {}", x); // x = 5
println!("y = {}", y); // y = 6
        

Изменяемость

По умолчанию связывание неизменяемое:

let x = 7;
x = 5; // Ошибка компиляции

Чтобы оно стало изменяемым - нужно использовать модификатор mut при связывании:

let mut x = 7;
x = 5;
println!("x = {}", x); // x = 5

Строгая типизация

Rust является статически типизированным языком программирования, однако чаще всего указывать тип вручную не требуется, поскольку в Rust имеется вывод типов из контекста.

Если всё же понадобилось указать тип вручную, то это можно сделать следующим образом:

let x: i32 = 10;
let x = 10i32;

Второй способ применим только для литералов

Владение

Связаные имена в Rust «владеют» тем, с чем они связаны. Когда имя выходит за область видимости, ресурс, с которым оно было связано, освобождается:

// Некоторый код до блока
{
  let v = Vec::<i32>::new();
}
// Ресурсы, выделенные для вектора v как  
// на стеке, так и в куче здесь уже освобождены

Семантика перемещения

Rust гарантирует, что у ресурса всегда существует ровно одно связывание. Если мы попробуем связать один ресурс с другим именем, то владение ресурсом будет передано новому связыванию, а старое перестанет быть актуальным:

let v = Vec::<i32>::new();
let v2 = v;
println!("{:?}", v); // Ошибка компиляции
// Использование перемещённого значения

Причина по которой нельзя использовать значения после перемещения является основопологающей. В строке

let v = Vec::<i32>::new();

создаются некоторые данные для вектора на стеке, однако сам буфер выделяется в куче, т.е. стековые данные содержат указатель на буфер в куче. При перемещении

let v2 = v;

создаётся копия стековых данных, в том числе и копия указателя на буфер в куче, что недопустимо.

Также стоит заметить, что компилятор оптимизирует подобные ситуации, так что реального копирования данных чаще всего не проиходит.

Заимствование

Механизм заимствования в Rust похож на механизмы ссылок из других языков программирования, но при ближайшем рассмотрении обнаруживаются существенные отличия. Так, например, следующий код не скомпилируется, просто потому что на данные существует «ссылка»:

let mut x = 7;
let y = &x;
x = 5; // Невозможно изменить x
// Так как x позаимствован

Множественное заимствование

Множественное заимствование олицетворяет ещё одну идею, лежащую в основе системы владения: на ресурс может существовать либо сколько угодно неизменямых ссылок, либо только одна изменяемая.

let mut x = 7;
let y = &x;
let z = &y;
println!("x = {}", x); // > x = 7
println!("y = {}", y); // > y = 7
println!("z = {}", z); // > z = 7

Но если хотя бы одно заимствование поменять на изменяемое, то код не скомпилируется:

let y = &mut x;
let z = &y;
println!("x = {}", x); // Ошибка: невозможно
        // заимствовать x как неизменяемый, 
        // так как x заимствован как изменяемый 
println!("y = {}", y); 
println!("z = {}", z); 

Интересный момент заключается в том, что z является ссылкой на ссылку, при этом разыменование, происходящее в вызове println «раскручивает» последовательность ссылок до данных. Эта особенность языка выходит далеко за рамки данной презентации и заслуживает отдельного внимания

Время жизни

Очень важно, что заимствование имеет своё время жизни, которое всегда меньше либо равно времени жизни данных:

let mut y = &10;
{
    let x = 1; 
    y = &x;
}
println!("{}", y); // Ошибка: x живёт 
                   // недостаточно долго

Система владения

Обобщая ранее сказанное, система владения в Rust основывается на следующих идеях:

Это позволяет компилятору в каждый момент времени знать кто владеет ресурсом, позаимствовал ли его кто-то, изменяемо или нет, когда ресурс выйдет из области видимости и прочее.

А поскольку такая информация доступна во время компиляции для каждого выделенного ресурса в программе, то компилятор может сам подставить вызовы, освобождающие ресурс, в места выхода ресурса из области видимости.

Вот такое автоматическое управление памятью без накладных расходов во время выполнения.

Источники

Официальный сайт www.rust-lang.org

Книга The Rust Programming Language и её перевод Язык Программирования Rust русскоязычным сообществом RuRust

Code Rust - Be Awesome ;)

Powered by ImpressJS Fork me on GitHub