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

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

type test_rec = {
    fld1: uint,
    fld2: str,
    fld3: char
};

Эксперимент 1. В руководстве по Rust сказано, что компилятор самостоятельно выбирает способ передачи параметров в функцию. Данные небольшого размера, например int копируются, данные большего размера, например записи, неявно передаются по ссылке.

fn chk1(data: test_rec) {
    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
Address of data = 0x9A5B840
{fld1=10, fld2=str, fld3=a}

Да, совершенно верно, адреса обоих записей совпадают, т.е. данные действительно были неявно переданы по-ссылке.

Эксперимент 2. А как поведет себя та же структура, если ее вернуть из функции?

fn chk2() -> test_rec {
    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 = 0x9A5CE80
Address of data = 0x9A5B830
{fld1=10, fld2=str, fld3=a}

Адреса записей не совпадают, что, в принципе, ожидаемо. По умолчанию записи создаются на стеке, так что приходится делать копию. И тут возникает вопрос, что делать, если не хочется создавать копии довольно большой записи?

Эксперимент 3. Если хочется создать запись в памяти, то можно воспользоваться разделяемыми указателями (Shared boxes), которые создаются при помощи добавления символа @. Да. для доступа к данным, содержащимся в разделяемом указателе, его необходимо разыменовать.

fn chk3() -> @test_rec {
    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
Address of data = 0x9A5CF78
{fld1=10, fld2=str, fld3=a}

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

Leave a Reply