Reputation: 10520
Is there any way to accomplish something like the following:
let v = vec![1, 2, 3];
let (a, b) = v.iter().take(2);
Such that a = 1
and b = 2
at the end?
I know I could just use a vector but I would like to have named variables.
Upvotes: 42
Views: 29846
Reputation: 764
The itertools crate has methods like tuples
and next_tuple
that can help with this.
use itertools::Itertools; // 0.9.0
fn main() {
let v = vec![1, 2, 3];
let (a, b) = v.iter().next_tuple().unwrap();
assert_eq!(a, &1);
assert_eq!(b, &2);
}
Upvotes: 33
Reputation: 1637
This may not be exactly what you asked for, but I suppose you rarely want to convert an arbitrarily large vector to a tuple anyway. If you just want to extract the first few elements of a vector into a tuple, you can do so using slice pattern matching:
fn main() {
let v = vec![1, 2, 3];
let (a, b) = match &v[..] {
&[first, second, ..] => (first, second),
_ => unreachable!(),
};
assert_eq!((a, b), (1, 2));
}
Upvotes: 26
Reputation: 6852
gcp is on the right track; his answer seems like the correct one to me.
I'm going to give a more compelling example, though, since the OP seemed in a comment to wonder whether what he asked for is even worthwhile ("I can't think of a good enough reason for this functionality to be possible."). Check out the Person::from_csv
function below:
use itertools::Itertools;
#[derive(Debug)]
struct Person<'a> {
first: &'a str,
last: &'a str,
}
impl<'a> Person<'a> {
// Create a Person from a str of form "last,first".
fn from_csv(s: &'a str) -> Option<Self> {
s.split(',').collect_tuple().map(
|(last, first)| Person { first, last }
)
}
}
fn main() {
dbg!(Person::from_csv("Doe")); // None
dbg!(Person::from_csv("Doe,John")); // Some(...)
dbg!(Person::from_csv("Doe,John,foo")); // None
}
It takes the Iterator produced by split
and collects the results into a tuple so that we can match and destructure it. If there are too many or too few commas, you won't get a matching tuple. This code is clean because collect_tuple
lets us use pattern matching and destructuring.
Here it is in the playground.
Upvotes: 2
Reputation: 1307
I wrote this ugly recursive macro that converts a Vec
to a tuple because I wanted to learn something about macros.
macro_rules! tuplet {
{ ($y:ident $(, $x:ident)*) = $v:expr } => {
let ($y, $($x),*) = tuplet!($v ; 1 ; ($($x),*) ; ($v[0]) );
};
{ $v:expr ; $j:expr ; ($y:ident $(, $x:ident)*) ; ($($a:expr),*) } => {
tuplet!( $v ; $j+1 ; ($($x),*) ; ($($a),*,$v[$j]) )
};
{ $v:expr ; $j:expr ; () ; $accu:expr } => {
$accu
}
}
I am new to this and probably very bad at it, so there's most likely a better way to do it. This is just a proof of concept. It allows you to write:
fn main() {
let v = vec![1, 2, 3];
tuplet!((a, b, c) = v);
assert_eq!(a, 1);
assert_eq!(b, 2);
assert_eq!(c, 3);
}
Somewhere in that macro definition you find the part $v[$j]
, which you could replace by $v.nth($j)
if you want to use it for iterators.
Upvotes: 6