【问题标题】:Why did compiler not error on this mutable borrow when there is an immutable borrowed string slice reference still in scope?当不可变借用字符串切片引用仍在作用域内时,为什么编译器不会在此可变借用上出错?
【发布时间】:2019-05-26 00:21:18
【问题描述】:

我正在从The Rust Programming Language book available from No Starch Press 学习 Rust,但遇到了一个问题,即编译器的行为与本书第 4 章 p. 77.

本书的第 4 章讨论了所有权,以及第 4 页的示例。 77 与 main() 中没有最终的 println!() 类似(我还添加了 cmets 和第 76 页中的函数来创建 MCVE)。 I also created a 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——所以这可能是编译器引入的一个变化,但我是试图理解为什么。为什么像现在这样出错而不是书中所说的出错更好?

明确地说,我自己理解错误。我试图理解为什么它不会在本书所说的位置引发编译器错误(即,为什么在编译器行为?)。

【问题讨论】:

  • 是因为NLL功能,
  • 请注意,您是在 2018 年版中进行编码。这本书是 2015 年版的主要书籍。
  • @Stargateur 非常令人失望,因为这本书几个月前才出版......在 2018 年!
  • @Dan 很抱歉给您带来不便,如果您想了解所有差异,这里有一个很好的资源,doc.rust-lang.org/edition-guide/introduction.html。有很棒的新功能所以不要失望;)

标签: rust ownership borrowing


【解决方案1】:

这是由于 non-lexical lifetimes 的更改,这是在最新版本的 Rust 中进行的更新(如果我没记错的话,在 Rust 1.31 引入的 2018 版中已稳定)。

在早期版本的 Rust(包括本书所基于的版本)中,任何引用都应该在创建它的整个范围内都是有效的(即,直到括号结束)。如果您使用word 删除该行并尝试在旧版本上编译代码,则会发出相同的错误——“借用为可变而借用为不可变”。

现在,借用检查器跟踪引用是否真正被使用。如果您在s.clear() 之后没有使用word,则假定可以在s.clear() 采用可变引用之前安全地删除对s 的不可变引用,因此,正如您所提到的,这段代码将被安全编译。当println! 存在时,借用检查器会看到不可变借用和可变借用的作用域相交,并准确地告诉你——注意错误分为三个部分:

  1. 不可变借用开始,
  2. 可变借用开始,
  3. 使用不可变借用。

【讨论】:

    猜你喜欢
    • 2016-05-02
    • 1970-01-01
    • 2022-11-26
    • 2021-08-09
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多