为什么编译器没有在这个可变借用时报错,当一个不可变的字符串切片引用仍然在作用域内?

5
我正在学习Rust,参考了No Starch Press出版的The Rust Programming Language一书。但是在第四章的第77页中,编译器的行为与书中讲解不符,遇到了问题。
该书的第四章讨论所有权,在第77页的示例类似于这个示例,只是在main()中没有最后的println!()(我还添加了注释和第76页的函数以创建MCVE)。我还创建了一个playground
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!()`,它也不应该编译)。但只要我在`clear()`操作后不尝试使用对`word`的引用,它就可以正常编译。
这本书使用的是Rust 1.21.0(请参见第2页),而我使用的是Rust 1.31.0-所以这可能是编译器引入的更改,但我正在努力理解为什么。为什么它现在抛出错误比书中说的位置更好?
明确一点,我理解错误本身。我正在尝试理解为什么它没有在书中指定的位置抛出编译器错误(即为什么编译器行为发生了变化?)。

1
这是因为NLL特性。 - Ömer Erden
请注意,您正在编写2018版的代码。该书主要是针对2015版的。 - Stargateur
@Stargateur 非常令人失望,因为这本书仅在几个月前出版... 在2018年! - Dan
1
@Dan 对于不便我们感到抱歉,如果你想了解所有的区别,这里有一个很好的资源 https://doc.rust-lang.org/edition-guide/introduction.html。有一些很棒的新功能,所以不要太失望了 ;) - Stargateur
1个回答

6
这是由于Rust最新版本中的非词汇生命周期更新所导致的更改(如果我没记错,已经在引入Rust 1.31的2018版中稳定下来)。
在较早的Rust版本中(包括本书所基于的版本),任何引用都应该在其创建的作用域内保持活动状态(即直到封闭大括号结束)。如果您删除了使用word的代码行,并尝试在旧版本上编译代码,则会发出相同的错误提示——“borrowed as mutable while borrowed as immutable”。
现在,借用检查器会跟踪引用是否真正被使用。如果您在s.clear()之后没有再使用word,则可以假定对s的不可变引用在s.clear()获取可变引用之前就可以安全地丢弃,因此,正如您所提到的那样,此代码将被安全编译。当存在println!时,借用检查器会看到不可变和可变借用的作用域相交,并向您指出——请注意,该错误分为三个部分:
  1. 不可变借用的开始,
  2. 可变借用的开始,
  3. 不可变借用的使用.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接