Управление памятью в Rust построено довольно необычно, поэтому хотелось бы написать про него пару слов. Для начала приведу описание структуры, которая будет использоваться для наглядных экспериментов:
fld1: uint,
fld2: str,
fld3: char
};
Эксперимент 1. В руководстве по Rust сказано, что компилятор самостоятельно выбирает способ передачи параметров в функцию. Данные небольшого размера, например int копируются, данные большего размера, например записи, неявно передаются по ссылке.
io::println(#fmt("Address of data = 0x%X", ptr::addr_of(data) as uint));
io::println(#fmt("{fld1=%u, fld2=%s, fld3=%c}", data.fld1, data.fld2, data.fld3));
}
// ...
let data1 = {fld1: 10u, fld2: "str", fld3: 'a'};
io::println(#fmt("Address of data = 0x%X", ptr::addr_of(data1) as uint));
chk1(data1);
// ...
Вывод в консоль:
Address of data = 0x9A5B840
{fld1=10, fld2=str, fld3=a}
Да, совершенно верно, адреса обоих записей совпадают, т.е. данные действительно были неявно переданы по-ссылке.
Эксперимент 2. А как поведет себя та же структура, если ее вернуть из функции?
let data = {fld1: 10u, fld2: "str", fld3: 'a'};
io::println(#fmt("Address of data = 0x%X", ptr::addr_of(data) as uint));
ret data;
}
// ...
let data2 = chk2();
io::println(#fmt("Address of data = 0x%X", ptr::addr_of(data2) as uint));
io::println(#fmt("{fld1=%u, fld2=%s, fld3=%c}", data2.fld1, data2.fld2, data2.fld3));
// ...
Вывод в консоль:
Address of data = 0x9A5B830
{fld1=10, fld2=str, fld3=a}
Адреса записей не совпадают, что, в принципе, ожидаемо. По умолчанию записи создаются на стеке, так что приходится делать копию. И тут возникает вопрос, что делать, если не хочется создавать копии довольно большой записи?
Эксперимент 3. Если хочется создать запись в памяти, то можно воспользоваться разделяемыми указателями (Shared boxes), которые создаются при помощи добавления символа @. Да. для доступа к данным, содержащимся в разделяемом указателе, его необходимо разыменовать.
let data = @{fld1: 10u, fld2: "str", fld3: 'a'};
io::println(#fmt("Address of data = 0x%X", ptr::addr_of(*data) as uint));
ret data;
}
// ...
let data3 = chk3();
io::println(#fmt("Address of data = 0x%X", ptr::addr_of(*data3) as uint));
io::println(#fmt("{fld1=%u, fld2=%s, fld3=%c}", data3.fld1, data3.fld2, data3.fld3));
Вывод в консоль:
Address of data = 0x9A5CF78
{fld1=10, fld2=str, fld3=a}
Как видно из примера, запись создалась в хипе и будет автоматически уничтожена по выходу из области видимости всех разделямых указателей ссылающихся на нее.