Почему компилятор не допустил ошибку при этом изменяемом заимствовании, когда в области действия остается неизменная зарезервированная ссылка на фрагмент строки?
Я изучаю Rust из книгиThe Rust Programming Language, доступной в No Starch Press, но столкнулся с проблемой, из-за которой компилятор не вел себя так, как описано в книге в главе 4 на с. 77.
Глава 4 книги обсуждает право собственности, а пример на с. 77 похоже на это без финала println!()
в main()
(Я также добавил комментарии и функцию из стр. 76 для создания MCVE). Я также создал игровую площадку.
fn main() {
let mut s = String::from("Hello world!");
let word = first_word(&s);
// according to book, compiler should not allow this mutable borrow
// since I'm already borrowing as immutable, but it does allow it
s.clear();
// but of course I do get error here about immutable borrow later being
// used here, but shouldn't it have errored on the clear() operation before
// it got here?
println!("First word of s is \"{}\"", word);
}
// return string slice reference to first word in string or entire string if
// no space found
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}
Я понимаю, почему компилятор выдает ошибку, где он в настоящее время делает. Но я понимаю из книги, что это должно было вызвать ошибку компилятора, когда я пытался очистить строку, потому что я не могу заимствовать s
как изменяемый, потому что он также заимствован как неизменяемый, таким образом исключая возможность ошибки, которую я получил (то есть, это не должно компилироваться даже без моего финального println!()
). Но это хорошо для меня, если я не пытаюсь использовать ссылку на word
после clear()
операция.
В книге используется Rust 1.21.0 (см. Стр. 2), тогда как я использую Rust 1.31.0 - так что, скорее всего, это изменение было внесено в компилятор, но я пытаюсь понять, почему. Почему лучше ошибаться, как в настоящее время, по сравнению с тем, что в книге сказано, что это будет ошибкой?
Чтобы было понятно, я понимаю сами ошибки. Я пытаюсь понять, почему он не выдает ошибку компилятора в том месте, которое, как говорится в книге, должно (т.е. почему изменяется поведение компилятора?).
1 ответ
Это изменение связано с нелексическими временами жизни, обновлением, сделанным в последних версиях Rust (стабилизировано в выпуске 2018 года, представленном с Rust 1.31, если я не ошибаюсь).
В более ранних версиях Rust (включая ту, на которой основана книга) предполагалось, что любая ссылка действительна для всей области, в которой она была создана (то есть до конца прилагаемых фигурных скобок). Если вы удалили строку с помощью word
и попытаться скомпилировать код на старой версии, он выдаст ту же ошибку - "заимствовано как изменяемое, а заимствовано как неизменяемое".
Теперь средство проверки заимствования отслеживает, действительно ли используется ссылка. Если вы не использовали word
после s.clear()
, предполагается, что неизменная ссылка на s
может быть безопасно отброшен раньше s.clear()
принимает изменяемый, так что, как вы упомянули, этот код будет безопасно скомпилирован. Когда println!
там, средство проверки заимствований видит, что область неизменяемых и изменяемых заимствований пересекается, и точно говорит вам об этом - обратите внимание, что ошибка делится на три части:
- начало неизменного заимствования,
- начало изменяемого заимствования,
- использование неизменных заимствований.