Давайте сначала немного исправим терминологию.
Привязки переменных не имеют времени жизни. Они имеют область действия . Когда привязка переменной выходит из области видимости, объект, к которому она привязывается, будет удален. Это поведение не контролируется заемщиком. Области являются лексическими, и их легко увидеть, глядя на исходный код.
Заимствования имеют время жизни. Эти времена жизни будут определены средством проверки заимствований, и они не привязаны к лексической структуре кода («нелексические времена жизни»), хотя они были в прошлом. Время жизни примерно простирается от момента создания заимствования до момента его последнего использования.
Когда вы создаете заем для переменной, переменная помечается как заимствованная средством проверки заимствования. В течение продолжительности жизни заимствования, как следует из средства проверки заимствований, вы не можете изменять или перемещать заимствованную переменную независимо от того, объявили ли вы привязку переменной как изменчивую или нет. Если вы создали общий заем, вы можете создавать дополнительные общие заимствования. Однако изменяемые заимствования являются исключительными: переменная, которая является заимствованной переменной, не может быть использована каким-либо другим способом в течение срока действия заимствования.
Эти правила гарантируют, что только одна «ручка» для переменной - либо привязка или изменяемый заем - может использоваться для изменения переменной в любой момент времени. Это фундаментальный инвариант Руста. Тот факт, что область привязки изменяемой переменной может перекрываться со временем жизни заимствования, не меняет этого, поскольку вы не можете изменять переменную через ее привязку в течение времени жизни заимствования.
Строго говоря, нет такой вещи как "изменяемая переменная"; есть только привязок изменяемых переменных . Если у вас есть право собственности на объект, вы всегда можете привязать его к нему, чтобы изменить его:
let s = "Hello".to_owned();
let mut s = s;
s.push_str(", world!");
В вашем коде есть еще одна тонкость: println!()
- это макрос, а не вызов функции. Он расширяется до некоторого кода, который только заимствует аргументов, которые вы передаете println!()
. Так что, хотя кажется, что вы передаете право владения s
на println!()
, на самом деле это не так. Если вместо этого вы используете макрос, вступающий во владение, код перестает компилироваться:
let mut s = String::from("hello");
let r = &s;
dbg!(r, s);
, что приводит к ошибке
error[E0505]: cannot move out of `s` because it is borrowed
--> src/main.rs:4:5
|
3 | let r = &s;
| -- borrow of `s` occurs here
4 | dbg!(r, s);
| ^^^^^^^^^^^
| |
| move out of `s` occurs here
| borrow later used here
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
См. Также: