从 Option 中提取可变引用

3

是否有选项可以从 Option<&mut Foo> 中提取可变引用? 我找到的只有 as_ref(),它提取了一个不可变引用。


1
要从选项中获取可变引用,可以使用 as_mut。但是你能描述一下你打算拥有一个 &'static mut Foo 的情况吗? - Denys Séguret
as_mut()并不返回可变引用,它只是将&mut Option<Foo>转换为Option<&mut Foo>。我需要的是一种方法来从Option<&mut Foo>中提取&mut Foo - fptech20
如果 let Some(r) = option { - user3840170
是的,那可能会起作用。我在想是否有更符合习惯的方式,可以减少样板代码。 - fptech20
这个样板代码的目的是明确地处理错误,而不是忽略它们。如果您的函数结构良好并且也返回OptionResult,那么使用?运算符提取内部值而不需要任何样板代码。 - Alexey Larionov
1
如果你知道OptionSome,那么不美观的&mut **opt.as_mut().unwrap()将从Option<&mut Foo>中提取出&mut Foo。(第二个*是为了解引用&mut &mut Foo,而&mut *显式重新借用,它提取可变引用而不移动它。)https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7af28c1073275a9232c08436b3c21e63 - user4815162342
2个回答

10

有没有办法从Option<&mut Foo>中提取可变引用。

有的,但需要一些努力来防止使用Option时出现错误,因为&mut T不是Copy类型。

我们假设你有一个Option<&mut Foo>,它是Some。(如果是非Some选项,则将unwrap()替换为适当的匹配即可。)例如:

let mut foo = Foo;
let mut opt = Some(&mut foo);

opt.unwrap()将会给你一个&mut Foo,但它会把它从Option移动出来,因为&mut Foo不是Copy类型:

let mut_ref = opt.unwrap();
drop(mut_ref);          // done with mut_ref
println!("{:?}", opt);  // XXX doesn't compile, opt is consumed

这将导致 Optionmut_ref 超出作用域后无法使用。我们不想这样,我们想要借用选项内部并在放弃借用后保留对 Option 的使用。为此,我们可以使用Option :: as_mut
let mut_ref_ref = opt.as_mut().unwrap();
drop(mut_ref_ref);
println!("{:?}", opt);  // compiles

这里有两个需要注意的地方:首先,你需要使用 as_mut() 而不是你尝试使用的 as_ref(),因为 as_ref() 会给你一个可变引用后面跟着一个共享引用,这使其无用。第二,解包 as_mut() 会返回一个 &mut &mut Foo,而不是我们想要的 &mut Foo。它非常接近 - 例如,自动解引用允许你调用一个接受 &mut Foo 的函数:

fn wants_ref(_r: &mut Foo) {}
let mut_ref_ref = opt.as_mut().unwrap();  // got &mut &mut Foo
wants_ref(mut_ref_ref);  // but we can send it to fn expecting &mut Foo

如果你因某种原因想要一个真正的&mut Foo,那么你应该通过对&mut &mut Foo进行解引用来获得它。然而,这样做是行不通的:

// XXX doesn't compile
let mut_ref = *opt.as_mut().unwrap();

那段代码无法编译,因为*试图对&mut &mut Foo进行取消引用,并提取底层的&mut Foo。但是&mut Foo不是Copy,所以这就是企图“移出可变引用”的行为,而rustc将告诉您这是不允许的。但是,在这种情况下可以这样做,您需要执行显式reborrow,也就是将EXPR转换为&mut *EXPR,其中EXPR计算为可变引用。 &mut *EXPR将创建对相同数据的可变引用。这不算别名,因为旧的可变引用会(静态地不可用)直到新的可变引用被丢弃。这基本上是允许在可变引用上工作的投影机制(即let x_ref = &mut point.x,其中point&mut Point)- 但应用于对象本身。

使用显式reborrow如下:

// `&mut *` is reborrow, and `*` dereferences `&mut &mut Foo`
let mut_ref = &mut **opt.as_mut().unwrap();
wants_ref(mut_ref);
println!("{:?}", opt);  // opt is still usable

游乐场


1
正确的方法是通过as_mut() 方法进行操作。
有一个棘手的问题,你需要满足&mut self参数类型。关键观察是,如果你拥有一个Option<&mut Foo>值,你可以安全地将其转换为同一类型的mut版本(因为你是它唯一的所有者)。 也就是说,你可以自由地从Option<&mut Foo>(所有权,不可变)转换为mut Option<&mut Foo>(所有权,不可变),然后在其上调用.as_mut()以达到&mut Foo(引用,可变)。
所以总体来看,它看起来像这样:
let maybe_foo: Option<&mut Foo> = Some(&mut foo);

let mut maybe_foo = maybe_foo;
if let Some(mutable_reference_to_foo) = maybe_foo.as_mut() {
    ...
}

游乐场


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