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

Управление памятью в C происходит либо путем объявления переменной, либо путем выделения фрагмента пространства во время выполнения. Затем то же самое можно освободить, используя явные вызовы функций. Таким образом, в основном контроль остается за разработчиком, что может привести к возникновению ошибок в коде. В таких языках, как Java, есть понятие сборщика мусора. Сборщик мусора — это процесс, который отслеживает программу на наличие утечек памяти и освобождает ее. Говорят, что память подходит для сборки мусора, если на нее не осталось ссылок или если она находится на острове изоляции. Сборщик мусора всегда работает в фоновом режиме, и его нельзя явно контролировать для освобождения памяти, что может немного выйти из-под контроля.

Собственность, с другой стороны, отличается от вышеупомянутой. Это не требует от вас явной работы с памятью или замедления программы при ее обработке. Он работает вокруг концепции масштаба.

Область видимости — это диапазон строк кода, в пределах которого допустим элемент. Как правило, мы помечаем область с помощью { фигурных скобок }. Таким образом, переменная, например, будет действительна с момента объявления до конца ее области видимости.

{ // s is not valid here let s = "hello"; // s is valid from this point forward // do stuff with s } // s is no longer valid

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

Коду доступно два типа памяти: стек и куча. Как правило, когда используемые данные имеют фиксированный известный размер, мы помещаем их в стек. Поскольку стек всегда обращается к данным «сверху», весь этот процесс становится быстрее и эффективнее. Но бывают случаи, когда фиксированный размер превышает предел стека или, возможно, нам требуется выделить память во время выполнения, и здесь мы используем кучу. Требуемая память в куче отслеживается ОС и, если она доступна, помечается как используемая. После этого мы помещаем указатель на эту память кучи в стек. Так как это требует от нас доступа к памяти с помощью указателя, весь процесс выполняется медленнее. Поэтому, когда мы вызываем функцию, значения параметров, локальные переменные и указатели на кучу помещаются в стек.

Таким образом, всякий раз, когда переменная покидает свою область видимости, она извлекается, и для данных в стеке все становится довольно просто. Память возвращается в ОС, как только владелец выходит из области действия. Однако все по-другому, когда данные хранятся в куче. В Rust есть специальный метод под названием drop, который возвращает ОС память, полученную владельцем в куче, dropвызывается всякий раз, когда rust встречает закрывающий '}'.

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

Перемещение: взаимодействие между данными и переменной

В приведенном ниже фрагменте кода:

let first_variable = 5; let second_variable = first_variable;

Мы связываем first_variable с ячейкой памяти, которая ссылается на значение 5, а затем мы создаем копию значения в first_variable и связываем ее со second_variable. Поскольку целые числа имеют постоянный размер и могут легко размещаться в стеке, это так же просто, как кажется. Однако, когда вы рассматриваете сложные типы данных, такие как строка, все немного по-другому. Строка может быть известна во время выполнения, например, при вводе данных пользователем, поэтому лучше выделить для нее память в куче. Чтобы идентифицировать эту память в куче, мы используем три детали: указатель на кучу, длину и емкость. Эта информация помещается в стек.

let s1 = String::from("hello"); //This is how we create a string // in Rust let s2 = s1;

Поэтому, когда мы пытаемся сделать что-то похожее на целочисленный пример, вместо копирования самой строки мы копируем информацию об идентификаторе, которую помещаем в стек. Итак, теперь есть две переменные, ссылающиеся на одну и ту же ячейку памяти. Теперь все становится интереснее. Что делать, если s1 выходит за рамки? Он должен вызывать метод drop(), верно? Здорово. Теперь, что происходит с s2? К какой ячейке памяти это относится? Что произойдет сейчас, когда s2 выйдет за рамки? Какое место в памяти вызовет его drop() свободным?

Вот где Rust играет по-другому. Как только вторая переменная указывает на то же место, Rust делает предыдущую переменную недействительной. Таким образом, в приведенном выше случае s1 становится недействительным, как только появляется s2. Следовательно, ничего не происходит, когда s1 выходит за пределы области действия, drop() вызывается только тогда, когда s2 выходит за пределы области действия. Таким образом, вместо поверхностной копии это можно назвать перемещением.

Передача параметров в функции

Еще одним интересным результатом этой ситуации владения является то, как она ведет себя в случае вызовов параметрических функций. Передача переменной в качестве параметра функции обрабатывается аналогично move, как показано выше. Таким образом, в основном, когда переменная String передается в качестве параметра функции, ее действительность в текущей области действия прекращается по умолчанию.

fn main() { let s = String::from("hello"); // s comes into scope takes_ownership(s); // s' value moves into the // function and so is no longer // valid here let x = 5; // x comes into scope makes_copy(x); // x would move into the function, // but i32 is Copy, so it’s okay to // still use x afterward } // Here, x goes out of scope, then s. But because s's value was // moved, nothing special happens. fn takes_ownership(some_string: String) { // some_string comes into //scope println!("{}", some_string); } // Here, some_string goes out of scope and `drop` is called. // The backing memory is freed. fn makes_copy(some_integer: i32) { // some_integer comes into scope println!("{}", some_integer); } // Here, some_integer goes out of scope. Nothing special happens.

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

Ссылки: https://doc.rust-lang.org/book/2018-edition

Первоначально опубликовано на blog.knoldus.com 29 октября 2018 г.