Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to shorten match expressions of non-Copy types using Option::and_then?

Tags:

rust

The Option::and_then function allows simplifying this code:

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

into this:

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

If I try the same thing with Strings, it doesn't compile:

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

However, the corresponding match expression works:

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

Is there a way to shorten this match expression like my first example with integers?

Code on playground

  • In this minimal example, I could have used map, but in my real code I'm calling another function that returns an Option so I really need and_then. It's just that I didn't want to over-complicate the example with an extra function that didn't affect the problem.

  • I really need to use foo afterwards, otherwise there wouldn't be any problem (actually, foo is captured by a closure that I need to use more than once, and Man! did I have a hard time tracking down why the compiler kept refusing my code! The error the trait FnMut... is not implemented for the type [closure@...] doesn't give much indication into why it isn't).

  • I used clone in the example because I wanted a simple operation using the string. In the real code, foo is not a string (it's a Regex) and I'm not cloning it in the closure (I'm applying it on a string and processing the results). Moreover, this code will be called a large number of times so avoiding unnecessary allocations and copies is important.

like image 701
Jmb Avatar asked Oct 07 '15 15:10

Jmb


People also ask

What does the Paste Options button do?

That thing–the Paste Options button –is your friend, a worker bee and not a fly whose only job is to follow your formatting instructions. Learning how it works keeps you from wasting time manually formatting pasted text.

How do you match a hyphen in a regex?

The hyphens within the regex are entered as literal characters and will be matched as such. After crafting our regex pattern, we’ll store that pattern into a variable called phoneNumRegex. We’ll then store phoneNumRegex into mo which is a special variable that returns the matched object.

How do you match a phone number with a regex pattern?

You can see that we’re repeating \d three and four times in sequence which will actually match the same pattern a North American phone number follows. The hyphens within the regex are entered as literal characters and will be matched as such. After crafting our regex pattern, we’ll store that pattern into a variable called phoneNumRegex.


1 Answers

Explanation

First of all: The method you actually want to use is map here, since you only want to change the inner value. and_then is useful if you create another Option in the closure.

To answer your question: It's correct that you can't access foo anymore. If you look at the function declaration...

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

... you see that the first argument is self. This means that the method consumes self (acquires the ownership) so foo is moved into the method and can't be used anymore.

Solution

If you only need bar afterwards (which is usually the case), you should just print bar. If you really need foo, too, you can do this:

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

as_ref creates a new Option that only holds a reference to the original inner variable. References are Copy types, so that Option can be safely consumed by map.

like image 59
Lukas Kalbertodt Avatar answered Sep 24 '22 08:09

Lukas Kalbertodt