为什么借贷检查器不允许第二笔可变借贷,即使第一笔已经超出范围? [英] Why does the borrow checker disallow a second mutable borrow even if the first one is already out of scope?

查看:51
本文介绍了为什么借贷检查器不允许第二笔可变借贷,即使第一笔已经超出范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道借阅检查器不允许多于一项可变借阅.例如,下面的代码无效:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆