无法解包一个共享引用后面的值。

23

这是我正在尝试执行的代码:

fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    if arg1.is_none() {
        return 0;
    }
    let integer = arg1.unwrap();
    *integer
}

fn main() {
    let integer = 42;
    my_fn(&Some(Box::new(integer)));
}

在 Rust 以前的版本中,我会得到以下错误:

(在 Rust playground 上查看)

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:5:19
  |
5 |     let integer = arg1.unwrap();
  |                   ^^^^ cannot move out of borrowed content

在更现代的版本中:

error[E0507]: cannot move out of `*arg1` which is behind a shared reference
 --> src/main.rs:5:19
  |
5 |     let integer = arg1.unwrap();
  |                   ^^^^
  |                   |
  |                   move occurs because `*arg1` has type `std::option::Option<std::boxed::Box<i32>>`, which does not implement the `Copy` trait
  |                   help: consider borrowing the `Option`'s content: `arg1.as_ref()`

我看到已经有很多关于借用检查器问题的文档,但是阅读后,我仍然无法解决问题。

为什么会出现错误,如何解决?

2个回答

23
Option::unwrap()方法会消耗选项(consume the option),也就是说,它通过值接受选项。但是,你没有一个值,只有对它的引用。这就是错误的原因。
你的代码应该按照惯用方式编写,如下所示:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    match arg1 {
        Some(b) => **b,
        None => 0,
    }
}

fn main() {
    let integer = 42;
    my_fn(&Some(Box::new(integer)));
}

(在 Rust playground 上)

或者你可以像 Shepmaster 建议的那样,使用 Option 组合器,例如Option::as_refOption::as_mut 搭配 Option::map_or

fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    arg1.as_ref().map_or(0, |n| **n)
}

这段代码利用了自动可复制的特性。如果Box内部的类型不是Copy,那么你将无法按值获取内部值,只能通过克隆或返回引用来获取,例如像这样:

fn my_fn2(arg1: &Option<Box<i32>>) -> &i32 {
    arg1.as_ref().map_or(&0, |n| n)
}

由于您只拥有对该选项的不可变引用,因此只能返回其内容的不可变引用。Rust 足够聪明,将文字 "0" 提升为静态值以保持能够在缺少输入值的情况下返回它。


如果我想获取盒子里面 i32 的引用(比方说我想修改它并让程序的另一部分看到更改后的值),该怎么办? - Moebius
1
对于此问题,我会写成arg1.as_ref().map(|x| **x).unwrap_or(0) - Shepmaster
1
@Moebius,我已经更新了我的答案,并提供了一个带有参考文献的示例,但是我并没有完全理解您所说的修改值的含义。提供一个示例会很有帮助,但这可能需要另一个问题来解决。 - Vladimir Matveev
@VladimirMatveev 没有特殊的用例,我只是出于好奇而问。感谢您提供的解决方案! - Moebius

1
自 Rust 1.40 起,有 Option::as_deref,因此现在可以这样做:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
    *arg1.as_deref().unwrap_or(&0)
}

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