为什么匹配的可变引用的条件赋值会导致借用错误? [英] Why does conditional assignment of a matched mutable reference cause borrow errors?

查看:35
本文介绍了为什么匹配的可变引用的条件赋值会导致借用错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解为什么这会导致错误:

#[派生(调试)]pub结构节点{下一个:Option>,}pub fn print_root_or_next(root: &mut Node, try_next: bool) {让 mut current = root;匹配 &mut current.next {一些(节点)=>{如果 try_next {当前 = &mut *node;}}无 =>返回,}println!("{:?}", 当前);}

error[E0502]: 不能借用 `current` 作为不可变的,因为它也被借用为可变的-->src/lib.rs:17:22|8 |匹配 &mut current.next {|----------------- 此处发生可变借用...17 |println!("{:?}", 当前);|^^^^^^^^|||不可变借用发生在这里|可变借用后来在这里使用

我看不出有冲突的借用;甚至错误消息似乎表明两个借用是一个并且相同.这是借用检查器的问题还是这个例子实际上存在某种缺陷?

我对这个限制很感兴趣.我对实现了解不多,但考虑到一个基本的 if:

if try_next {current = &mut *current.next.as_mut().unwrap();}

和基本的match:

match &mut current.next {一些(节点)=>{当前 = &mut *node;}无 =>返回,}

甚至颠倒它们:

if try_next {匹配 &mut current.next {一些(节点)=>{当前 = &mut *node;}无 =>返回,}}

所有的工作,一定有一些事情是借阅检查员正在考虑或没有考虑的,这与我对原始表格为什么不起作用的理解相冲突.

解决方案

我认为问题在于 current 是自身的条件再借用.

考虑这个删除条件但使用两个变量的更简单的代码:

#[派生(调试)]pub结构节点{下一个:Option>,}pub fn print_root_or_next(root: &mut Node) {让 a = 根;让 b;匹配 &mut a.next {一些(节点)=>{b = &mut *node;}无 =>返回,}println!("{:?}", a);println!("{:?}", b);}

这失败了,正如您所期望的错误消息:

error[E0502]: 不能借用 `a` 作为不可变的,因为它也被借用为可变的-->src/lib.rs:16:22|9 |匹配 &mut a.next {|----------- 可变借用发生在这里...16 |println!("{:?}", a);|^ 不可变借用发生在这里17 |println!("{:?}", b);|- 此处使用可变借用

也就是说,a 是可变借用的,并且该借用由于 b 而保持活动状态.所以你不能在 b 存活时使用 a.

如果你对最后两行 println! 重新排序,它编译没有问题,因为词法生命周期:b 被打印然后被遗忘,释放借用并制作a 再次可用.

现在,看看另一个变体,类似于您的变体,但没有 if:

pub fn print_root_or_next(root: &mut Node) {让 mut c = 根;匹配 &mut c.next {一些(节点)=>{c = &mut *node;}无 =>返回,}println!("{:?}", c);}

它也可以很好地编译,因为当重新借用 c 时,它会被重新分配.从这一点开始,它就像前面例子的 b 一样工作.你可以随意使用那个b,禁止使用的是a,这里不再提供.

回到你的代码:

pub fn print_root_or_next(root: &mut Node, test: bool) {让 mut c = 根;匹配 &mut c.next {一些(节点)=>{如果测试{c = &mut *node;}}无 =>返回,}println!("{:?}", c);}

这里的问题是,当c被重新借用时,它是有条件地完成的,编译器不知道它会是哪一个,比如a还是b 来自上面的例子.所以它必须假设它是同时的!但是从示例中我们看到,当 b 处于活动状态时,您不能使用 a,但由于它们都是相同的值,因此无法再使用此值.>

当编译器抱怨这个奇怪的错误信息时:

17 |, 当前的);|^^^^^^^^|||不可变借用发生在这里|可变借用后来在这里使用

这实际上意味着:

17 |, 当前的);|^^^^^^^^|||如果 try_next 为 false,则此处发生不可变借用|如果 try_next 为真,这里同时使用可变借用

我不知道这是否是编译器的限制,使用这个有条件地重新借用自身的引用实际上是安全的.也许这是检查借用的限制,或者可能有一些我不明白的微妙之处......我怀疑如果您包含多个条件或多个引用,则允许这样做可能是不合理的.

I'm having trouble understanding why this causes an error:

#[derive(Debug)]
pub struct Node {
    next: Option<Box<Node>>,
}

pub fn print_root_or_next(root: &mut Node, try_next: bool) {
    let mut current = root;
    match &mut current.next {
        Some(node) => {
            if try_next {
                current = &mut *node;
            }
        }
        None => return,
    }

    println!("{:?}", current);
}

error[E0502]: cannot borrow `current` as immutable because it is also borrowed as mutable
  --> src/lib.rs:17:22
   |
8  |     match &mut current.next {
   |           ----------------- mutable borrow occurs here
...
17 |     println!("{:?}", current);
   |                      ^^^^^^^
   |                      |
   |                      immutable borrow occurs here
   |                      mutable borrow later used here

I can't see how there are conflicting borrows; even the error message seems to indicate that two borrows are one and the same. Is this an issue in with the borrow checker or is this example actually flawed in some way?

I am very interested in this limitation. I don't know much about the implementation, but considering that a basic if:

if try_next {
    current = &mut *current.next.as_mut().unwrap();
}

and basic match:

match &mut current.next {
    Some(node) => {
        current = &mut *node;
    }
    None => return,
}

and even inverting them:

if try_next {
    match &mut current.next {
        Some(node) => {
            current = &mut *node;
        }
        None => return,
    }
}

all work, there must be some thing that the borrow checker is or is not considering that conflicts with my understanding of why the original form doesn't work.

解决方案

I think that the problem is that current is a conditional reborrow of itself.

Consider this simpler code that removes the condition but uses two variables:

#[derive(Debug)]
pub struct Node {
    next: Option<Box<Node>>,
}

pub fn print_root_or_next(root: &mut Node) {
    let a = root;
    let b;
    match &mut a.next {
        Some(node) => {
            b = &mut *node;
        }
        None => return,
    }

    println!("{:?}", a);
    println!("{:?}", b);
}

This fails, as you would expect with the error message:

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
  --> src/lib.rs:16:22
   |
9  |     match &mut a.next {
   |           ----------- mutable borrow occurs here
...
16 |     println!("{:?}", a);
   |                      ^ immutable borrow occurs here
17 |     println!("{:?}", b);
   |                      - mutable borrow later used here

That is, a is mutably borrowed and that borrow is kept alive because of b. So you cannot use a while b is alive.

If you reorder the last two println! lines, it compiles without issues, because of the lexical lifetime: b is printed and then forgotten, releasing the borrow and making a available again.

Now, look at this other variant, similar to yours but without the if:

pub fn print_root_or_next(root: &mut Node) {
    let mut c = root;
    match &mut c.next {
        Some(node) => {
            c = &mut *node;
        }
        None => return,
    }
    println!("{:?}", c);
}

It compiles fine, too, because when c is reborrowed it is reassigned. From this point on, it works as the b of the previous example. And you could use that b freely, what was forbidden was using a, and that is no more available here.

Back to your code:

pub fn print_root_or_next(root: &mut Node, test: bool) {
    let mut c = root;
    match &mut c.next {
        Some(node) => {
            if test {
                c = &mut *node;
            }
        }
        None => return,
    }
    println!("{:?}", c);
}

The issue here is that when c is reborrowed, it is done conditionally, the compiler does not know which one it will be, like a or like b from the example above. So it must assume that it is both at the same time! But from the example we saw that you cannot use a while b is alive, but since they are both the same value, this value just cannot be used any longer.

When the compiler complains with this weird error message:

17 | , current);
   | ^^^^^^^
   | |
   | immutable borrow occurs here
   | mutable borrow later used here

It actually means:

17 | , current);
   | ^^^^^^^
   | |
   | immutable borrow occurs here if try_next is false
   | mutable borrow at the same time used here if try_next is true

I don't know if this is a limitation of the compiler and using this reference that conditionally reborrows itself is actually safe. Maybe it is a limitation of the borrow checked, or maybe there is some subtlety that I don't understand... I suspect that allowing this might be unsound if you include more than one condition or more than one reference.

这篇关于为什么匹配的可变引用的条件赋值会导致借用错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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