Почему компилятор не допустил ошибку при этом изменяемом заимствовании, когда в области действия остается неизменная зарезервированная ссылка на фрагмент строки?

Я изучаю 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! там, средство проверки заимствований видит, что область неизменяемых и изменяемых заимствований пересекается, и точно говорит вам об этом - обратите внимание, что ошибка делится на три части:

  1. начало неизменного заимствования,
  2. начало изменяемого заимствования,
  3. использование неизменных заимствований.
Другие вопросы по тегам