Is there an easy/readable way to move multiple values from nested option without cloning (because copying vectors is costly)?
Consider code like this
struct A {
pub b: Option<B>
}
struct B {
pub c: Option<C>,
pub d: Option<C>,
pub e: Option<C>,
}
struct C {
pub v1: Option<Vec<i64>>,
pub v2: Option<Vec<i64>>,
}
struct Result {
pub v11: Option<Vec<i64>>,
pub v22: Option<Vec<i64>>,
}
fn main() {
let a= Some(A {
b: Some(B {
c: Some(C {
v1: Some(vec![1, 2, 3]),
v2: Some(vec![4, 5, 6]),
}),
d: Some(C {
v1: Some(vec![7, 8, 9]),
v2: Some(vec![10, 11, 12]),
}),
e: Some(C {
v1: Some(vec![13, 14, 15]),
v2: Some(vec![16, 17, 18]),
}),
}),
});
// Construct Result such that v11 = v1 from c and v22 = v2 from d if all fields are Some
let result = Result {
v11: a.and_then(|a| a.b).and_then(|b| b.c).and_then(|c| c.v1),
v22: a.and_then(|a| a.b).and_then(|b| b.d).and_then(|d| d.v2),
};
}
This obviously fails on
error[E0382]: use of moved value: `a`
--> src/main.rs:42:14
|
22 | let a= Some(A {
| - move occurs because `a` has type `Option<A>`, which does not implement the `Copy` trait
...
41 | v11: a.and_then(|a| a.b).and_then(|b| b.c).and_then(|c| c.v1),
| - ----------------- `a` moved due to this method call
| |
| help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
42 | v22: a.and_then(|a| a.b).and_then(|b| b.d).and_then(|d| d.v2),
| ^ value used here after move
|
I could simultaneously move (c,d)
from a.b
separately and then move from those into the Result
, but for even larger (deeper, more branched) nested structures this gets absolutely unreadable.
Reason why I need this: I would like to "flatten" a protobuf message (giant structure of Option
s), filter out some fields and transform some values without copying everything.
I propose a two-step approach:
let (c, d) = a
.and_then(|a| a.b)
.map(|b| (b.c, b.d))
.unwrap_or((None, None));
let result = Result {
v11: c.and_then(|c| c.v1),
v22: d.and_then(|d| d.v2),
};
It pulls the c
and d
values from the a.b
chain at the same time (with None
if they don't exist) so it can then use them separately for filling in the Result
.
Or a different formulation that does the last and_then
s inside the map
:
let (v11, v22) = a
.and_then(|a| a.b)
.map(|b| (
b.c.and_then(|c| c.v1),
b.d.and_then(|d| d.v2),
))
.unwrap_or((None, None));
let result = Result { v11, v22 };