Reputation: 21727
I have a struct
struct Foo {
foo1: String,
foo2: String,
foo3: String,
foo4: String,
// ...
}
I would like to create an instance of Foo
from a vector.
let x = vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()];
match x.as_slice() {
&[ref a, ref b, ref c, ref d] => {
let foo = Foo {
foo1: a.to_string(),
foo2: b.to_string(),
foo3: c.to_string(),
foo4: d.to_string(),
};
},
_ => unreachable!(),
}
Do I have to copy the strings? Is there any better way to destructure the vector into a
, b
, c
, d
as well as transferring the ownership?
Actually, I don't mind x
is completely destroyed after the destructuring. So I hope there is a pattern match for vectors apart from slices as well. For now it seems we can only destructure slices.
Upvotes: 8
Views: 3936
Reputation: 47
The question is old but for anyone who is looking for a better answer: There is no way to do this as wanted.
The solution is to split the pattern matching and destructuring in 2 steps:
let x = vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()];
match x.len() {
4 => {
let [a, b, c, d] = x.try_into().unwrap();
//let foo = Foo {...};
},
// For at least 4 elements, we can use range and .take(n)
4.. => {
let [a, b, c, d] = x.take(4).try_into().unwrap();
//let foo = Foo {...};
},
_ => unreachable!(),
}
if you want some more complex pattern matching, you may need to duplicate the destructuring, once for the check and another for the actual destructuring.
Upvotes: 0
Reputation: 2177
You can use the TryFrom
implementation of arrays to do this:
let x = vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()];
let [a, b, c, d] = <[String; 4]>::try_from(x).expect("Vec didn't have 4 elements");
Upvotes: 1
Reputation: 430673
Do I have to copy the strings?
Not if you are willing to give up destructuring. I'm a big fan of itertools:
use itertools::Itertools; // 0.8.2
fn main() {
let x = vec![
"a".to_string(),
"b".to_string(),
"c".to_string(),
"d".to_string(),
];
if let Some((foo1, foo2, foo3, foo4)) = x.into_iter().tuples().next() {
let foo = Foo {
foo1,
foo2,
foo3,
foo4,
};
}
}
This transfers ownership of the vector (and thus the members) to an iterator, then the tuples
adapter chunks up the values into a tuple. We take the first one of those and construct the value.
You could also use drain
if you didn't want to give up ownership of the entire vector:
if let Some((foo1, foo2, foo3, foo4)) = x.drain(..4).tuples().next() {
Is there any better way to destructure the vector into
a
,b
,c
,d
as well as transferring the ownership?
No, there is no mechanism to take ownership of a part of a Vec
without creating another Vec
(or another type that has the same limits) except for an iterator.
Upvotes: 7
Reputation: 16188
Destructuring slices isn't stable, and you can't move out of a slice because it's just a borrow — if you moved out, what would the Vec
's destructor do?
Mutating the vector is the way to go here:
let mut x = vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()];
let foo = Foo {
foo4: x.pop().unwrap(),
foo3: x.pop().unwrap(),
foo2: x.pop().unwrap(),
foo1: x.pop().unwrap(),
};
println!("{:?}", foo);
Upvotes: 1