Как правило, заем будет действовать до тех пор, пока он не выйдет за рамки, которые он определил. При программировании то, что мы обычно называем «областью действия», формально известно как Lexical Scope .
До того как ржавчина получила Non Lexical Lifetimes , подробно объясненное в ссылке предоставленный Jmb , ваш пример не будет компилироваться ни в одном случае. Это на самом деле делает заимствование немного легче в освоении, поскольку правила более последовательны, но на практике намного раздражают.
Вы можете попробовать это самостоятельно, используя Godbolt Compiler Explorer, установив ржавчину c версии <1.31. Пример этого можно найти <a href="https://rust.godbolt.org/z/LtRKaZ" rel="nofollow noreferrer"> здесь . Как вы видите, это дает ошибку компилятора в версии 1.30.
Пример 1
let mut a = String::from("hello");
let b = &mut a;
println!("b1 {}", b);
println!("a1 {}", a);
Одним из способов решения этой проблемы было добавление новой лексической области видимости. например:
Example2
let mut a = String::from("hello");
{
let b = &mut a;
println!("b1 {}", b);
}
println!("a1 {}", a);
В этом случае эксклюзивный заем &mut a
будет действовать только до конца лексической области действия, в которой он находился.
Итак, из примера 1 видно, что мы на самом деле не используем заем &mut a
после println!("b1 {}", b);
, поэтому для нас, программистов, интуитивно понятно, что программисту приходится вставлять область видимости только для того, чтобы объяснить это компилятору. строги.
Кроме того, если мы объявляем любую другую переменную после let b = &mut a;
, мы хотим использовать ее после того, как ее область действия не будет работать, поэтому нам нужно адаптироваться и изменять способ написания наших программ только для удовлетворения проверки заимствования .
Руст позже получил то, что называется Non Lexical Lifetimes, и это именно то, что написано на лейбле. Время жизни, которое не привязано строго к Lexical Scope, в котором оно находится.
Теперь компилятор проверяет, когда вы прекращаете использовать заем, и вставляет невидимую «область действия времени жизни» (или, по крайней мере, вы можете думать об этом так) так же, как в примере 2, но без недостатков введения истинного лексического контекста.
Вот почему вы испытываете немного "волхвов c", которые могут показаться немного удивительными в вашем примере. Я попытался объяснить, показав область действия компилятора, чтобы проверить, разрешено или нет общее &
указатель на c
.
Однако, поверьте мне, это небольшое дополнение к Кривая изучения о контроллере заимствований абсолютно стоит того, как только вы поймете, какие обручи вам нужно пройти без него.
let mut c = String::from("hello");
println!("c1 {}", &c);
c = String::from("hello2");
println!("c2 {}", &c);
// "lifetime scope" of `&mut c`
let e = &mut c; //{ |----------------------
println!("e1 {}", &e); // | |
// | |
e.pop(); // | |
// | |
println!("e2 {}", &e); // | |
// println!("c {}", &c); // | | (e3)
// | |
{ // | (e4) |
e.pop(); // | |
e.pop(); // | |
// | |
println!("e3 {}", &e); // }<-----|-------
println!("c3 {}", &c); // |
} // |
// |
// println!("e4 {}", &e);// }<------
println!("c4 {}", &c);
Я ясно показал, что println!
берет заем и не не нужно брать на себя ответственность, чтобы сделать код немного понятнее, когда речь идет о временах жизни и заимствованиях.
Обычно передача c
вместо &c
функции передает владение, но println!
- это макрос (не функция), которая в этом случае только повторно заимствует его. Различие может быть важным при изучении заимствований и продолжительности жизни.