Перейти к содержанию

Время жизни и память

Posted on:6 февраля 2023 г. at 14:04

Example Dynamic OG Image link

Типы и перемещения

Память приложений:

Переменные:

let t = S(1);

Перемещение:

let a = t;

Безопасность типов:

let c: S = M::new();

Область действия и удаление:

{
    let mut c = S(2);
    c = S(3);  // <- Удаление `c` перед назначением.
    let t = S(1);
    let a = t;
}   // <- Область действия `a`, `t`, `c` заканчивается здесь и вызывается удаление для `a`, `c`.

Стек вызовов

Границы функций:

fn f(x: S) { … }

let a = S(1); // <- Мы здесь
f(a);

Вложенные функции:

fn f(x: S) {
    if once() { f(x) } // <- Мы здесь (перед рекурсией)
}

let a = S(1);
f(a);

Перепрофилирование памяти:

fn f(x: S) {
    if once() { f(x) }
    let m = M::new() // <- We are here (after recursion)
}

let a = S(1);
f(a);

Ссылки и указатели

Ссылки в качестве указателей:

let a = S(1);
let r: &S = &a;
let r: &S = &a;
let r = &a;

Доступ к памяти, не принадлежащей владельцам:

let mut a = S(1);
let r = &mut a;
let d = r.clone();  // Допустимо для клонирования (или копирования) из r.
*r = S(2);          // Допустимо установить новое значение S в r.

Ссылки на защищенные ссылки:

let mut a = …;
let r = &mut a;
let d = *r;       // недопустимое значение для перемещения, «a» будет пустым.
*r = M::new();    // недопустимо для хранения значение, отличного от S, не имеет смысла.

Необработанные указатели:

let p: *const S = questionable_origin();

Основы жизненного цикла

«Жизнь» событий:

Типичное времени жизни:

{
    let b = S(3);
    {
        let c = S(2);
        let r: &'c S = &c;      // не совсем работает, так как мы не можем назвать локальное время жизни
        {                       // переменные в теле функции, но применяется тот же принцип
            let a = S(0);       // для выполнения функций.

            r = &a;             // расположение 'a' не имеет достаточного количества строк жизни - > не ok.
            r = &b;             // расположение 'b' содержит все строки жизни 'c' и более - > ok.
        }
    }
}

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

let mut b = S(0);
let r = &mut b;

b = S(4);   // Потерпит неудачу, так как b в заимствованном состоянии.

print_byte(r);

Время жизни в функциях

Параметры функции:

fn f(x: &S, y:&S) -> &u8 { … }

let b = S(1);
let c = S(2);

let r = f(&b, &c);

Проблема «заимствованного» распространения:

let b = S(1);
let c = S(2);

let r = f(&b, &c);

let a = b;   // Мы можем это сделать?
let a = c;   // Какой из них действительно заимствован?

print_byte(r);

Время жизни распространяет заимствованное состояние:

fn f<'b, 'c>(x: &'b S, y: &'c S) -> &'c u8 { … }

let b = S(1);
let c = S(2);

let r = f(&b, &c); // Мы знаем, что возвращенная ссылка основана на c, которая должна оставаться заблокированной,
                   // в то время как b может свободно перемещаться.

let a = b;

print_byte(r);

Разблокирование:

Продвинутый

Ссылки на ссылки:

// Возвращает ближнюю ('b) ссылку.
fn f1sr<'b, 'a>(rb: &'b     &'a     S) -> &'b     S { *rb }
fn f2sr<'b, 'a>(rb: &'b     &'a mut S) -> &'b     S { *rb }
fn f3sr<'b, 'a>(rb: &'b mut &'a     S) -> &'b     S { *rb }
fn f4sr<'b, 'a>(rb: &'b mut &'a mut S) -> &'b     S { *rb }

// Возвращает ближнюю ('b) изменяемую ссылку.
// f1sm<'b, 'a>(rb: &'b     &'a     S) -> &'b mut S { *rb } // M
// f2sm<'b, 'a>(rb: &'b     &'a mut S) -> &'b mut S { *rb } // M
// f3sm<'b, 'a>(rb: &'b mut &'a     S) -> &'b mut S { *rb } // M
fn f4sm<'b, 'a>(rb: &'b mut &'a mut S) -> &'b mut S { *rb }

// Возвращает дальнюю ('a) ссылку.
fn f1lr<'b, 'a>(rb: &'b     &'a     S) -> &'a     S { *rb }
// f2lr<'b, 'a>(rb: &'b     &'a mut S) -> &'a     S { *rb } // L
fn f3lr<'b, 'a>(rb: &'b mut &'a     S) -> &'a     S { *rb }
// f4lr<'b, 'a>(rb: &'b mut &'a mut S) -> &'a     S { *rb } // L

// Возвращает дальнюю ('a) изменяемую ссылку.
// f1lm<'b, 'a>(rb: &'b     &'a     S) -> &'a mut S { *rb } // M
// f2lm<'b, 'a>(rb: &'b     &'a mut S) -> &'a mut S { *rb } // M
// f3lm<'b, 'a>(rb: &'b mut &'a     S) -> &'a mut S { *rb } // M
// f4lm<'b, 'a>(rb: &'b mut &'a mut S) -> &'a mut S { *rb } // L

// Теперь предположим, что у нас где-то есть `ra`.
let mut ra: &'a mut S = …;

let rval = f1sr(&&*ra);       // Нормально
let rval = f2sr(&&mut *ra);
let rval = f3sr(&mut &*ra);
let rval = f4sr(&mut ra);

//  rval = f1sm(&&*ra);       // Было бы плохо, так как 'rval' будет изменяемой
//  rval = f2sm(&&mut *ra);   // ссылкой, полученной из нарушаемой мутабельной
//  rval = f3sm(&mut &*ra);   // цепочки.
let rval = f4sm(&mut ra);

let rval = f1lr(&&*ra);
//  rval = f2lr(&&mut *ra);   // Если бы это сработало, у нас были бы 'rval' и 'ra' …
let rval = f3lr(&mut &*ra);
//  rval = f4lr(&mut ra);     // … теперь (mut) псевдоним 'S' в вычислении ниже.

//  rval = f1lm(&&*ra);       // То же, что и выше, не удается по причинам мутабельной цепочки.
//  rval = f2lm(&&mut *ra);   //                    "
//  rval = f3lm(&mut &*ra);   //                    "
//  rval = f4lm(&mut ra);     // То же, что и выше, не удается из-за наложения псевдонимов.

// Какое-то вымышленное место, где мы используем 'ra' и 'rval', обе действующие.
compute(ra, rval);

Здесь (M) означает сбой компиляции из-за ошибки мутабельности, (L) ошибки времени жизни. Кроме того, разыменовывание *rb не является строго необходимым, просто добавлен для ясности.

Удаление и _:

{
    let f = |x, y| (S(x), S(y)); // Функция, возвращающая два «Droppables».

    let (    x1, y) = f(1, 4);  // S(1) - EoS   S(4) - EoS
    let (    x2, _) = f(2, 5);  // S(2) - EoS   S(5) - немедленно сброшено
    let (ref x3, _) = f(3, 6);  // S(3) - EoS   S(6) - EoS

    println!("…");
}

Здесь EoS означает, что время жизни будет до конца срока действия, т.е. после println!().