Что происходит в стеке, когда одно значение закрывает другое в Rust?

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

Я был ожидание, что следующее было ошибкой, но это не является:

for line in reader.lines() {
    let line = line.expect("Could not read line.");

Для полного контекста у меня есть весь код вкратце. Это код после того, как я все исправил, и соответствующие строки 37 и 38. Однако он требует подачи текстового файла в качестве аргумента.


Я ожидал ошибки, потому что line находится в стеке (по крайней мере, указатель). Правильно ли, что он все еще может быть уничтожено и заменено без жалоб?

Что происходит под капотом в отношении управления памятью и стеком? Я предполагать, что line на самом деле является ссылкой на строку (тип &str). Итак, это нормально, потому что в любом случае сам указатель — объект в стеке — это просто usize, так что оба line объекта имеют одинаковый размер в стеке.

Могу ли я сделать это с чем-то другого размера? Могла ли вторая строка сказать:

let line: f64 = 3.42;

В этом случае сам объект находится в стеке, и он потенциально больше, чем usize.

@Stargateur Я не думаю, что это дубликат, потому что мой основной вопрос не Зачем, это нормально, а как, это нормально? Как все происходит в стеке под капотом, чтобы это стало возможным? Есть просто 2 разных объекта, один старый line, а другой новый line, хотя старый больше недоступен? Или старый действительно перезаписывается (если в том же объеме)? Если да, то как это возможно для типов разного размера?

Mike Williamson 30.05.2019 06:26
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
3
1
473
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Всякий раз, когда переменная объявляется с помощью let, это совершенно новая переменная, отдельная от всего, что было до нее. Даже если переменная с таким именем уже существует, исходная переменная — затененный, пока новая переменная находится в области действия. Если переменная затенена, она обычно недоступна.

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

Мы можем увидеть это в действии в следующем примере.

#[derive(Debug)]
struct DroppedU32(u32);

impl Drop for DroppedU32 {
    fn drop(&mut self) {
        eprintln!("Dropping u32: {}", self.0);
    }
}

fn main() {
    let x = 5;
    dbg!(&x); // the original value
    {
        let x = 7;
        dbg!(&x); // the new value
    }
    dbg!(&x); // the original value again

    let y = DroppedU32(5);
    dbg!(&y); // the original value
    let y = DroppedU32(7);
    dbg!(&y); // the new value

    // down here, when the variables are dropped in
    // reverse order of declaration,
    // the original value is accessed again in the `Drop` impl.
}

(игровая площадка)

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

Код

pub fn add_three(x: u32, y: u32, z: u32) -> u32 {
    let x = x + y;
    let x = x + z;
    x
}

компилируется в

example::add_three:
        lea     eax, [rdi + rsi]
        add     eax, edx
        ret

Если вы похожи на меня и не слишком знакомы с кодом на ассемблере, это в основном

  1. Складывает x и y и помещает результат в переменную (назовем ее w).
  2. Добавляет z к w и перезаписывает w результатом.
  3. Возвращает ш.

Итак (помимо входных параметров), используется только одна переменная, хотя мы дважды использовали let x = .... Промежуточный результат let x = x + y; перезаписывается.

Спасибо, @SCappella! Это отвечает на наиболее моего замешательства, но теперь у меня другое. Я понимаю, что происходит логически, но не понимаю, что происходит в распределении памяти и абстракциях Rust с нулевой стоимостью. В случае, когда я тень, но в том же масштабе, как и в вашем первом примере (let x=5; ... let x=7), поскольку это та же область видимости, области, где x=5, больше нет. Итак, что в этом случае происходит с let x:i32 = 5... let x:u64 = 7? Мой вопрос: у меня был 32-битный элемент в стеке, который теперь является 64-битным элементом в той же позиции стека, верно? Как я могу это сделать?

Mike Williamson 30.05.2019 06:20

@MikeWilliamson Они не занимают одну и ту же позицию в стеке. Исходная переменная становится недоступной, но она по-прежнему существует в памяти без изменений. Я добавлю пример, чтобы показать это.

SCappella 30.05.2019 07:54

@MikeWilliamson Лучший способ подумать об этом - рассматривать внутреннюю x как совершенно другую переменную с другим именем. Представьте, что компилятор даже переименование внутренне (или ведет себя как как будто). Тогда очевидно, что они не занимают одну и ту же позицию в стеке, и нет проблем с тем, что они имеют разные типы.

user4815162342 30.05.2019 08:32

Отлично, спасибо @SCappella! Кстати, как вы можете получить байт-код или во что он компилируется? В Python я использовал библиотеку dis. Есть ли что-то подобное в Rust? И если да, то как это управляется, поскольку Rust компилируется под определенные архитектуры/операционные системы. (Будет ли одна и та же программа создавать разные байт-коды на разных платформах? Или байт-код является просто посредником, как в JVM или Python?)

Mike Williamson 31.05.2019 19:56

@MikeWilliamson Если вы хотите получить сборку с помощью Cargo, см. этот вопрос. Если вы согласны с онлайн-инструментами, ознакомьтесь с Проводник компилятора. Игровая площадка ржавчины также имеет параметры для создания сборки и других промежуточных представлений, которые использует Rust. Что касается других вопросов, попробуйте задать новый вопрос, и я уверен, что вы получите хороший ответ.

SCappella 31.05.2019 23:18

Другие вопросы по теме

Похожие вопросы