Почему законно брать временный?
Исходя из C++, я довольно удивлен, что этот код действителен в Rust:
let x = &mut String::new();
x.push_str("Hello!");
В C++ вы не можете взять адрес временного, и временный не переживет выражение, в котором он появляется.
Как долго временный живет в Русте? И с тех пор x
это только заем, кто владелец строки?
3 ответа
Почему законно брать временный?
Это законно по той же причине, что и в C++, потому что кто-то сказал, что так и должно быть.
Как долго временный живет в Русте? И с тех пор
x
это только заем, кто владелец строки?
время жизни временных значений обычно
- самое сокровенное заявление; хвостовое выражение блока считается частью оператора, который включает в себя блок, или
- выражение условия или условное выражение цикла, если временное создается в выражении условия
if
илиif
/else
или вloop
условное выражение выражения while.Когда создается временное значение, которое присваивается
let
декларация, однако, временная создается с временем жизни включающего блока вместо этого, как с помощью оператора включения (let
объявление) будет гарантированной ошибкой (так как указатель на временное будет храниться в переменной, но временное будет освобождено, прежде чем переменная может быть использована). Компилятор использует простые синтаксические правила, чтобы решить, какие значения присваиваются привязке let, и, следовательно, заслуживает более длительного временного времени жизни.
По сути, вы можете рассматривать свой код как:
let mut a_variable_you_cant_see = String::new();
let x = &mut a_variable_you_cant_see;
x.push_str("Hello!");
Смотрите также:
Из ржавчины
Временные жизни
При использовании выражения значения в большинстве контекстов выражения места создается временная безымянная область памяти, инициализированная для этого значения, и вместо этого выражение оценивается в этом месте
Это относится, потому что String::new()
является выражением значения и быть чуть ниже &mut
это находится в контексте выражения места. Теперь оператору ссылки нужно только пройти через эту временную ячейку памяти, поэтому он становится значением всей правой части (включая &mut
).
Однако, когда создается выражение временного значения, которое назначается в объявлении let, вместо этого создается временное значение с временем жизни включающего блока.
Поскольку он назначен переменной, он получает время жизни до конца включающего блока.
Это также отвечает на этот вопрос о разнице между
let a = &String::from("abcdefg"); // ok!
а также
let a = String::from("abcdefg").as_str(); // compile error
Во втором варианте временное передается в as_str()
так что его время жизни заканчивается в конце оператора.
MIR от Rust дает некоторое представление о природе временных; рассмотрим следующий упрощенный случай:
fn main() {
let foo = &String::new();
}
и MIR, который он производит (стандартные комментарии заменены на мои):
fn main() -> () {
let mut _0: ();
scope 1 {
let _1: &std::string::String; // the reference is declared
}
scope 2 {
}
let mut _2: std::string::String; // the owner is declared
bb0: {
StorageLive(_1); // the reference becomes applicable
StorageLive(_2); // the owner becomes applicable
_2 = const std::string::String::new() -> bb1; // the owner gets a value; go to basic block 1
}
bb1: {
_1 = &_2; // the reference now points to the owner
_0 = ();
StorageDead(_1); // the reference is no longer applicable
drop(_2) -> bb2; // the owner's value is dropped; go to basic block 2
}
bb2: {
StorageDead(_2); // the owner is no longer applicable
return;
}
}
Вы можете видеть, что "невидимый" владелец получает значение до того, как ему назначена ссылка, и что ссылка отбрасывается перед владельцем, как и ожидалось.
То, в чем я не уверен, так это то, почему есть, казалось бы, бесполезный scope 2
и почему владелец не попадает внутрь какой-либо области; Я подозреваю, что MIR просто еще не готов на 100%.