从Option<&mut T>多次读取引用

9

我有一个 Option<&mut T>,希望可以多次访问其中的引用:

fn f(a: Option<&mut i32>) {
    if let Some(x) = a {
        *x = 6;
    }
    // ...
    if let Some(x) = a {
        *x = 7;
    }
}

fn main() {
    let mut x = 5;
    f(Some(&mut x));
}

那样做是行不通的,因为if let Some(x) = a会将Option中的引用值移出,第二个if let Some(x) = a会导致编译器错误。没有第二个if let ...,这个程序可以完美运行,所以a不需要是可变的。
以下是:
if let Some(ref x) = a {
    **x = 6;
}

出现了一个错误:"无法将值分配给不可变引用"。

以下代码可以正常运行:

fn f(mut a: Option<&mut i32>) {
    if let Some(ref mut x) = a {
        **x = 6;
    }
    if let Some(ref mut x) = a {
        **x = 7;
    }
}
mut a是必要的,否则我会得到一个错误:“无法将不可变匿名字段 (a:std::prelude::v1::Some).0 借用为可变”。但这感觉不对:a 不应该是可变的,因为我没有修改它(参见上文)。
正确的解决方案是什么?
编辑1
我的问题与如何在不引起移动错误的情况下传递`Option<&mut ...>`到多个函数调用中? 不同。 我想多次可变地取消引用 Option<&mut T> 中的引用,而另一个问题则想将Option传递给多个函数调用。 另一个问题的解决方案不适用于我的情况。

其他答案都很好。真的。如果需要,只需将可变引用作为第一件事传递给您的参数即可。 - Shepmaster
你的意思是这样的:let tmp = &mut a; if let &mut Some(ref mut x) = tmp { ...?但是这样a必须是可变的,而我想要避免这种情况。 - Adrian Willenbücher
但是a必须是mut,那有什么问题吗?你函数外的任何人都不关心(或者不能关心)这个参数是否是mut。你拥有它,所以你可以随心所欲地使用它。如果你真的很在意,你也可以这样做:let mut tmp1 = a; let mut tmp2 = &mut tmp1; - Shepmaster
“是的,有什么问题吗?”我正在寻找比我已经找到的解决方案更好/更干净/更优雅的解决方案(这个解决方案在“This would work”下面)。 - Adrian Willenbücher
1个回答

4

这个怎么样?

fn f(a: Option<&mut i32>) {
    if let Some(&mut ref mut x) = a {
        *x = 6;
    }
    // ...
    if let Some(&mut ref mut x) = a {
        *x = 7;
    }
}

在这种情况下,a不需要是可变的。 &mut ref mut有点奇怪,但它很有道理:首先我们通过解构删除了&mut,然后再次对该值取一个可变引用。当我们不使用Option时,更容易理解。
let mr: &mut Vec<u32> = &mut vec![];
{
    let &mut ref mut a = mr;
    a.push(3);
}
mr.push(4);

这也可以正常工作。第三行(特殊)等同于:

let a = &mut     *mr   ;
//               ^^^----- this is an lvalue of type `Vec<u32>`
//      ^^^^^^^^^^^^----- together it's of type `&mut Vec<u32>` again

在`Option`的情况下,我们不能使用`&mut *X`版本,而需要在模式内完成所有操作。 因此是`&mut ref mut x`。

1
如果你觉得有道理的话,我不介意解释一下;)我知道&mut T不像&T那样是Copy的,但我不明白这种奇怪的操作如何帮助绕过move。是否会有一些重新借用的情况呢? - Matthieu M.
1
let-pattern 中的 &mut 解构了引用,接下来的 ref mut x 创建了一个指向 i32 值的新引用。这在概念上与读取 &mut i32 引用不同,但应该生成相同的指令。 - Adrian Willenbücher
@MatthieuM。我尝试更详细地解释一下 ^_^ 希望现在更清楚了。 - Lukas Kalbertodt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接