How do I take a Vec<Option<T>>
, where T
cannot be copied, and unwrap all the Some
values?
I run into an error in the map
step. I'm happy to move ownership of the original list and "throw away" the None
s.
#[derive(Debug)]
struct Uncopyable {
val: u64,
}
fn main() {
let num_opts: Vec<Option<Uncopyable>> = vec![
Some(Uncopyable { val: 1 }),
Some(Uncopyable { val: 2 }),
None,
Some(Uncopyable { val: 4 }),
];
let nums: Vec<Uncopyable> = num_opts
.iter()
.filter(|x| x.is_some())
.map(|&x| x.unwrap())
.collect();
println!("nums: {:?}", nums);
}
Playground
Which gives the error
error[E0507]: cannot move out of borrowed content
--> src/main.rs:17:15
|
17 | .map(|&x| x.unwrap())
| ^-
| ||
| |hint: to prevent move, use `ref x` or `ref mut x`
| cannot move out of borrowed content
In Rust, when you need a value, you generally want to move the elements or clone them.
Since move is more general, here it is, only two changes are necessary:
let nums: Vec<Uncopyable> = num_opts
.into_iter()
// ^~~~~~~~~~~~-------------- Consume vector, and iterate by value
.filter(|x| x.is_some())
.map(|x| x.unwrap())
// ^~~------------------ Take by value
.collect();
As llogiq points out, filter_map
is specialized to filter out None
already:
let nums: Vec<Uncopyable> = num_opts
.into_iter()
// ^~~~~~~~~~~~-------- Consume vector, and iterate by value
.filter_map(|x| x)
// ^~~----- Take by value
.collect();
And then it works (consuming num_opts
).
As pointed out by @nirvana-msu, in Rust 1.33 std::convert::identity
was added which can be used instead of |x| x
. From the documentation:
let filtered = iter.filter_map(identity).collect::<Vec<_>>();
You don't need to copy the Uncopyable
at all, if you are OK with using a Vec
of references into the original Vec
:
let nums: Vec<&Uncopyable> = num_opts.iter().filter_map(|x| x.as_ref()).collect();
// ^ notice the & before Uncopyable?
This may not do the trick for you if you have to work with an API that requires &[Uncopyable]
. In that case, use Matthieu M.'s solution which can be reduced to:
let nums: Vec<Uncopyable> = num_opts.into_iter().filter_map(|x| x).collect();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With