为什么当范围内仍然存在不可变的借用字符串切片引用时,编译器不会在此可变借用上出错?
Why did compiler not error on this mutable borrow when there is an immutable borrowed string slice reference still in scope?
我正在从 The Rust Programming Language book available from No Starch Press 学习 Rust,但 运行 遇到了一个问题,即编译器的行为与本书第 4 章中解释的不一样。 77.
本书第 4 章讨论所有权,以及第 1 页的示例。 77 与 main()
中没有最后的 println!()
类似(我还添加了注释和第 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——所以这可能是对编译器,但我试图理解 为什么 。为什么像现在这样犯错误比书中说的错误更好?
明确地说,我理解错误本身。我试图理解为什么它不会在书中所说的位置抛出编译器错误(即,为什么编译器行为会发生变化?)。
这是由于 non-lexical lifetimes 在最新版本的 Rust 中进行的更新(如果我没记错的话,在 Rust 1.31 引入的 2018 版中稳定)。
在 Rust 的早期版本(包括本书所基于的版本)中,任何引用都应该在创建它的整个范围内有效(即,直到大括号结束) .如果您使用 word
删除该行并尝试在旧版本上编译代码,它会发出相同的错误 — "borrowed as mutable while borrowed as immutable".
现在,借用检查器会跟踪引用是否真的被使用过。如果您在 s.clear()
之后没有使用 word
,则可以假定在 s.clear()
接受可变引用之前可以安全地删除对 s
的不可变引用,因此,你已经提到,这段代码将被安全地编译。当 println!
存在时,借用检查器会发现不可变和可变借用的作用域相交,并准确地告诉您 — 请注意错误分为三个部分:
- 开始不可变借用,
- 开始可变借用,
- 不可变借用的使用。
我正在从 The Rust Programming Language book available from No Starch Press 学习 Rust,但 运行 遇到了一个问题,即编译器的行为与本书第 4 章中解释的不一样。 77.
本书第 4 章讨论所有权,以及第 1 页的示例。 77 与 main()
中没有最后的 println!()
类似(我还添加了注释和第 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——所以这可能是对编译器,但我试图理解 为什么 。为什么像现在这样犯错误比书中说的错误更好?
明确地说,我理解错误本身。我试图理解为什么它不会在书中所说的位置抛出编译器错误(即,为什么编译器行为会发生变化?)。
这是由于 non-lexical lifetimes 在最新版本的 Rust 中进行的更新(如果我没记错的话,在 Rust 1.31 引入的 2018 版中稳定)。
在 Rust 的早期版本(包括本书所基于的版本)中,任何引用都应该在创建它的整个范围内有效(即,直到大括号结束) .如果您使用 word
删除该行并尝试在旧版本上编译代码,它会发出相同的错误 — "borrowed as mutable while borrowed as immutable".
现在,借用检查器会跟踪引用是否真的被使用过。如果您在 s.clear()
之后没有使用 word
,则可以假定在 s.clear()
接受可变引用之前可以安全地删除对 s
的不可变引用,因此,你已经提到,这段代码将被安全地编译。当 println!
存在时,借用检查器会发现不可变和可变借用的作用域相交,并准确地告诉您 — 请注意错误分为三个部分:
- 开始不可变借用,
- 开始可变借用,
- 不可变借用的使用。