为什么借贷检查器不允许第二笔可变借贷,即使第一笔已经超出范围? [英] Why does the borrow checker disallow a second mutable borrow even if the first one is already out of scope?
问题描述
我知道借阅检查器不允许多于一项可变借阅.例如,下面的代码无效:
I know that the borrow checker disallows more than one mutable borrows. For example, the code below is invalid:
fn main() {
let mut x = 42;
let a = &mut x;
let b = &mut x;
println!("{} {}", a, b);
}
但是,如果第一次借用由于超出范围而被删除,则第二次借用有效:
However, if the first borrow is dropped due to out of scope, the second borrow is valid:
fn main() {
let mut x = 1;
{
let a = &mut x;
println!("{}", a);
}
let b = &mut x;
println!("{}", b);
}
由于非词法生存期(NLL),第一次借用甚至不必超出范围-借阅检查器仅要求不再使用它.因此,以下代码在Rust 2018中有效:
Because of non-lexical lifetimes (NLL), the first borrow doesn't even have to be out of scope — the borrow checker only requires it not to be used anymore. So, the code below is valid in Rust 2018:
fn main() {
let mut x = 1;
let a = &mut x;
println!("{}", a);
let b = &mut x;
println!("{}", b);
}
问题
但是我不明白为什么下面的代码无效:
The Problem
But I don't understand why the code below is invalid:
use std::str::Chars;
fn main() {
let s = "ab".to_owned();
let mut char_iter = s.chars();
let mut i = next(&mut char_iter);
dbg!(i.next());
let mut j = next(&mut char_iter);
dbg!(j.next());
}
fn next<'a>(char_iter: &'a mut Chars<'a>) -> impl Iterator<Item = char> + 'a {
char_iter.take_while(|&ch| ch != ' ')
}
编译错误消息:
error[E0499]: cannot borrow `char_iter` as mutable more than once at a time
--> src/main.rs:10:22
|
7 | let mut i = next(&mut char_iter);
| -------------- first mutable borrow occurs here
...
10 | let mut j = next(&mut char_iter);
| ^^^^^^^^^^^^^^ second mutable borrow occurs here
11 | dbg!(j.next());
12 | }
| - first borrow might be used here, when `i` is dropped and runs the destructor for type `impl std::iter::Iterator`
从错误消息中,我认为NLL可能还不支持这种情况.因此,我提早放弃了 i
:
From the error message I thought that maybe NLL doesn't support this case yet. So, I dropped i
early:
use std::str::Chars;
fn main() {
let s = "ab".to_owned();
let mut char_iter = s.chars();
{
let mut i = next(&mut char_iter);
dbg!(i.next());
}
let mut j = next(&mut char_iter);
dbg!(j.next());
}
fn next<'a>(char_iter: &'a mut Chars<'a>) -> impl Iterator<Item = char> + 'a {
char_iter.take_while(|&ch| ch != ' ')
}
(生锈的操场)
但是我得到了一个更令人困惑的错误消息:
But I got a more confusing error message:
error[E0499]: cannot borrow `char_iter` as mutable more than once at a time
--> src/main.rs:12:22
|
8 | let mut i = next(&mut char_iter);
| -------------- first mutable borrow occurs here
...
12 | let mut j = next(&mut char_iter);
| ^^^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
为什么说先借用以后在这里使用
,即使 i
之前已经被删除并且超出范围?
Why does it say first borrow later used here
even if i
is already dropped and out of scope before?
如果我将 next
函数的签名更改为此,则会编译代码:
The code is compiled if I change the signature of next
function into this:
fn next(char_iter: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
char_iter.take_while(|&ch| ch != ' ')
}
但是,我仍然想了解为什么原来的 next
函数不起作用.
But still, I want to understand why the original next
function doesn't work.
推荐答案
让我们在这里解读这种类型演绎魔法. impl Iterator
实际上是一个具体的类型: Chars
,它用 TakeWhile
包装,因此您可以像这样重写您的方法(顺便说一句,以确定& char
的生存期):
Let's decipher this type deduction magic here. impl Iterator
is actually a concrete type: Chars
that wrapped with TakeWhile
, so you may rewrite your method like this (btw, an interesting task is to determine the &char
's lifetime):
fn next<'a>(
char_iter: &'a mut Chars<'a>,
) -> TakeWhile<&'a mut Chars<'a>, impl FnMut(&char) -> bool> {
char_iter.take_while(|&ch| ch != ' ')
}
现在,您可能会看到输出类型与输入一样长,反之亦然.实际上,该寿命是从最初使用的& str
导出的.因此,您可能会得出结论,只要使用的字符串(即到 main
的末尾),甚至显式的 drop(i)
都不会对结果类型有效.您,因为编译器知道 Chars
一直借用到最后.要使nll正常工作,您必须(不幸的是?)帮助编译器:
Now you may see that the output type lives as long as the input and vice versa. That lifetime, in fact, derived from the &str
that is originally used. Therefore, you may conclude that the resulted type lives as long as the used string (i.e. to the end of the main
) and even explicit drop(i)
won't help you, because compiler knows that the Chars
is borrowed till the end. For working nll you must (unfortunately?) help the compiler:
fn next<'a, 'b: 'a>(
char_iter: &'a mut Chars<'b>,
) -> TakeWhile<&'a mut Chars<'b>, impl FnMut(&char) -> bool> {
char_iter.take_while(|&ch| ch != ' ')
}
这篇关于为什么借贷检查器不允许第二笔可变借贷,即使第一笔已经超出范围?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!