Почему Rust не расширяет область действия временного значения, на которое ссылается val3 в следующем коде:
fn main() {
let mut vec = vec![1, 2,3];
let val1 = vec.get(0).unwrap_or(&0);
println!("{val1}");
let val2 = match vec.get_mut(0) {
Some(v) => v,
None => &mut 0,
};
println!("{val2}");
let val3 = vec.get_mut(0).unwrap_or(&mut 0);
println!("{val3}");
}
Компиляция дает следующую ошибку
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:13:46
|
13 | let val3 = vec.get_mut(0).unwrap_or(&mut 0);
| ^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
14 | println!("{val3}");
| ------ borrow later used here
|
help: consider using a `let` binding to create a longer lived value
|
13 ~ let binding = 0;
14 ~ let val3 = vec.get_mut(0).unwrap_or(&mut binding);
|
For more information about this error, try `rustc --explain E0716`.
error: could not compile `question` (bin "question") due to 1 previous error
Если посмотреть на код val1, это будет работать для неизменяемой ссылки с использованием unwrap_or(). Глядя на код val2, можно увидеть, что это будет работать для изменяемой ссылки с использованием выражения соответствия. Но очевидно, что это не работает с val3 для изменяемой ссылки с использованием unwrap_or(). Кто-нибудь знает причину разницы?
Если бы мне пришлось сделать предположение, я бы сказал, что выражение match работает, потому что изменяемая ссылка на временное значение немедленно присваивается переменной в операторе let. При использовании unwrap_or() эта ссылка на временное значение вместо этого передается функции. Если ссылка является неизменяемой, она не потребляет ссылку, и назначение по-прежнему работает. Если ссылка является изменяемой, unwrap_or() потребляет ссылку, и повторное заимствование не происходит, поскольку что-то является временным значением.
Короткий ответ: в случае val3
это не работает, потому что это не соответствует критериям временного продления жизни.
Сначала давайте разберемся со случаем val2
. Это описано в этом разделе по ссылке выше:
Для оператора let с инициализатором расширяющее выражение — это выражение, которое является одним из следующих:
- ...
- Окончательное выражение любого выражения расширяющегося блока.
Это «окончательное утверждение» рычага спичечного блока, поэтому оно соответствует критериям. Таким образом, время жизни временного объекта увеличивается до уровня времени жизни val2
.
В случае val3
он просто не соответствует критериям — в списке отсутствуют выражения вызова функций (что явно указано в следующем параграфе документации).
Так почему же это работает в случае val1
? Поскольку для этого требуется общая ссылка, а не эксклюзивная, она подлежит постоянной рекламе.
Перемещение выражения значения в слот
'static
происходит, когда выражение может быть записано в виде константы, заимствовано и разыменовано в том заимствовании, где выражение было изначально записано, без изменения поведения во время выполнения.
Другими словами, это фактически синтаксический сахар для:
static ZERO: i32 = 0;
let val1 = vec.get(0).unwrap_or(&ZERO);
Если вы запустили эту функцию много раз, все ссылки &0
будут указывать на одно и то же фактическое значение, которое будет переведено в хранилище статической длительности. Поскольку изменить значение через общую ссылку невозможно, проблем с этим нет.
Это не может работать с эксклюзивными ссылками — каждое эксклюзивно заимствованное временное содержимое должно быть уникальным, поскольку вы можете его мутировать, чтобы их нельзя было просто поместить в хранилище статической длительности и использовать совместно.