rust - 为什么借用仍然保留在 if let 的 else block 中?

标签 rust

为什么以下代码中的调用 self.f2() 会触发借用检查器? else block 不是在不同的范围内吗?这是一个难题!

use std::str::Chars;

struct A;

impl A {
    fn f2(&mut self) {}

    fn f1(&mut self) -> Option<Chars> {
        None
    }

    fn f3(&mut self) {
        if let Some(x) = self.f1() {

        } else {
            self.f2()
        }
    }
}

fn main() {
    let mut a = A;
}

Playground

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:16:13
   |
13 |         if let Some(x) = self.f1() {
   |                          ---- first mutable borrow occurs here
...
16 |             self.f2()
   |             ^^^^ second mutable borrow occurs here
17 |         }
   |         - first borrow ends here

self 的借用范围不是以 self.f1() 调用开始和结束吗?一旦来自 f1() 的调用返回,f1() 不再使用 self,因此借用检查器应该不会对第二次借用有任何问题。请注意以下代码也失败了...

// ...
if let Some(x) = self.f1() {
    self.f2()
}
// ...

Playground

我认为第二次借用在这里应该没问题,因为 f1f3 没有与 f2 同时使用 self

最佳答案

我在这里举了一个例子来展示范围规则:

struct Foo {
    a: i32,
}

impl Drop for Foo {
    fn drop(&mut self) {
        println!("Foo: {}", self.a);
    }
}

fn generate_temporary(a: i32) -> Option<Foo> {
    if a != 0 { Some(Foo { a: a }) } else { None }
}

fn main() {
    {
        println!("-- 0");
        if let Some(foo) = generate_temporary(0) {
            println!("Some Foo {}", foo.a);
        } else {
            println!("None");
        }
        println!("-- 1");
    }
    {
        println!("-- 0");
        if let Some(foo) = generate_temporary(1) {
            println!("Some Foo {}", foo.a);
        } else {
            println!("None");
        }
        println!("-- 1");
    }
    {
        println!("-- 0");
        if let Some(Foo { a: 1 }) = generate_temporary(1) {
            println!("Some Foo {}", 1);
        } else {
            println!("None");
        }
        println!("-- 1");
    }
    {
        println!("-- 0");
        if let Some(Foo { a: 2 }) = generate_temporary(1) {
            println!("Some Foo {}", 1);
        } else {
            println!("None");
        }
        println!("-- 1");
    }
}

这打印:

-- 0
None
-- 1
-- 0
Some Foo 1
Foo: 1
-- 1
-- 0
Some Foo 1
Foo: 1
-- 1
-- 0
None
Foo: 1
-- 1

简而言之,if 子句中的表达式似乎同时存在于 if block 和 else block 中。

一方面,这并不奇怪,因为它确实需要比 if block 更长寿,但另一方面,它确实阻止了有用的模式。

如果您更喜欢直观的解释:

if let pattern = foo() {
    if-block
} else {
    else-block
}

脱糖成:

{
    let x = foo();
    match x {
    pattern => { if-block }
    _ => { else-block }
    }
}

虽然您希望它脱糖成:

bool bypass = true;
{
    let x = foo();
    match x {
    pattern => { if-block }
    _ => { bypass = false; }
    }
}
if not bypass {
    else-block
}

你不是第一个被这个绊倒的人,所以尽管改变了一些代码的含义(特别是守卫),这可能会在某个时候得到解决。

关于rust - 为什么借用仍然保留在 if let 的 else block 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30698061/

相关文章:

performance - 防 rust 性能问题-高复杂度代码

rust - 如何在另一个文件中分离结构的默认特征的实现?

rust - 如何从枚举返回内部类型变量引用而又没有遍历泛型?

rust - 为什么变量范围取决于定义顺序?

rust - 如何使用索引位置压缩 Vec<T>

rust - 通过引用将 Vec 传递给函数

rust - 如何设置这些结构的生命周期?

rust - 为什么隐藏不释放借用的引用?

dns - 如何在 Rust 中检索不同类型的 DNS 记录?

function - 有没有办法避免取消对特征对象的引用?