有没有办法使用Option :: and_then缩短非复制类型的匹配表达式?

时间:2015-10-07 15:29:00

标签: rust

Option::and_then功能允许简化此代码:

let foo = Some(1);
let bar = match foo {
    Some(i) => Some(i + 1),
    None => None,
};
println!("Foo: {:?}", foo);

进入这个:

let foo = Some(1);
let bar = foo.and_then(|i| Some(i + 1));
println!("Foo: {:?}", foo);

如果我对String s尝试相同的操作,则无法编译:

let foo = Some("bla".to_string());
let bar = foo.and_then(|ref f| Some(f.clone()));
println!("Foo: {:?}", foo);
error[E0382]: use of moved value: `foo`
 --> src/main.rs:4:27
  |
3 |     let bar = foo.and_then(|ref f| Some(f.clone()));
  |               --- value moved here
4 |     println!("Foo: {:?}", foo);
  |                           ^^^ value used here after move
  |
  = note: move occurs because `foo` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait

但是,相应的match表达式有效:

let foo = Some("bla".to_string());
let bar = match foo {
    Some(ref f) => Some(f.clone()),
    None => None,
};
println!("Foo: {:?}", foo);

有没有办法缩短这个匹配表达式,就像我的第一个带整数的例子一样?

Code on playground

  • 在这个最小的例子中,我可以使用map,但在我的实际代码中,我正在调用另一个返回Option的函数,所以我真的需要and_then。只是我不想用一个不影响问题的额外功能使示例过于复杂。

  • 之后我真的需要使用foo,否则就不会有任何问题(实际上,foo被我需要多次使用的闭包捕获,而Man !我是否很难找到编译器拒绝我的代码的原因!错误the trait FnMut... is not implemented for the type [closure@...]没有说明为什么不能这样做。

  • 我在示例中使用了clone因为我想要使用字符串进行简单的操作。在实际代码中,foo不是一个字符串(它是Regex),我没有在闭包中克隆它(我将它应用于字符串并处理结果)。此外,此代码将被多次调用,因此避免不必要的分配和复制非常重要。

4 个答案:

答案 0 :(得分:13)

解释

首先:您实际想要使用的方法是map,因为您只想更改内部值。如果您在闭包中创建另一个and_then,则Option非常有用。

回答您的问题:您无法再访问foo,这是正确的。如果你看一下函数声明......

fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U>
//                                        ^^^^

...你看到第一个参数是self。这意味着该方法会消耗self(获取所有权),因此foo会被移入方法中,并且无法再使用。

解决方案

如果您之后只需要bar(通常就是这种情况),您应该只打印bar。如果你真的需要foo,你也可以这样做:

let bar = foo.as_ref().map(|s| s.clone());

as_ref创建一个新的Option,它只包含对原始内部变量的引用。引用为Copy类型,因此Option可以安全地使用map

答案 1 :(得分:4)

您想使用Option::as_ref

fn main() {
    let foo = Some("bla".to_string());
    let bar = foo.as_ref().and_then(|f| Some(f.clone()));
    println!("Foo: {:?}", foo);
}

答案 2 :(得分:0)

你可以克隆foo,并在结果上调用and_then

let bar = foo.clone().and_then (|f| Some (f));

答案 3 :(得分:0)

选项实现克隆,它可以按预期工作。

let foo = Some("bla".to_string());
let bar = foo.clone();