Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a tuple from a vector?

Tags:

tuples

rust

Here's an example that splits a string and parses each item, putting it into a tuple whose size is known at compile time.

use std::str::FromStr;

fn main() {
    let some_str = "123,321,312";
    let num_pair_str = some_str.split(',').collect::<Vec<&str>>();
    if num_pair_str.len() == 3 {
        let num_pair: (i32, i32, i32) = (
            i32::from_str(num_pair_str[0]).expect("failed to parse number"),
            i32::from_str(num_pair_str[1]).expect("failed to parse number"),
            i32::from_str(num_pair_str[2]).expect("failed to parse number"),
        );
        println!("Tuple {:?}", num_pair);
    }
}

Is there a way to avoid repetition parsing the numbers?

This is an example of what it might look like if Rust supported Python-like comprehensions:

let num_pair: (i32, i32, i32) = (
    i32::from_str(num_pair_str[i]).expect("failed to parse number")
    for i in 0..3
);

Is it possible to declare the tuple in a way that expands the vector?

like image 504
ideasman42 Avatar asked Aug 10 '16 03:08

ideasman42


People also ask

What is a vector tuple?

What is Vector of Tuple? A tuple is an object that can hold a number of elements and a vector containing multiple number of such tuple is called a vector of tuple. The elements can be of different data types. The elements of tuples are initialized as arguments in order in which they will be accessed.

Can you have a tuple of vectors in C++?

This article focuses on how to create a 2D vector of tuples in C++. A 2D vector of tuples or vector of vectors of tuples is a vector in which each element is a vector of tuples itself. Although a tuple may contain any number of elements for simplicity, a tuple of three elements is considered.

What is std :: tuple?

Class template std::tuple is a fixed-size collection of heterogeneous values. It is a generalization of std::pair. If std::is_trivially_destructible<Ti>::value is true for every Ti in Types , the destructor of tuple is trivial.


3 Answers

You can't use Python-like list comprehension, as Rust doesn't have it. The closest thing is to do it explicitly via another iterator. You can't directly collect into a tuple, so you need another explicit step to convert the vector:

use std::str::FromStr;

fn main() {
    let some_str = "123,321,312";
    let num_pair_str = some_str.split(',').collect::<Vec<_>>();
    if num_pair_str.len() == 3 {
        let v = num_pair_str.iter().map(|s| i32::from_str(s).expect("failed to parse number"))
            .collect::<Vec<_>>();
        let num_pair: (i32, i32, i32) = (v[0], v[1], v[2]);
        println!("Tuple {:?}", num_pair);
    }
}

If you want to avoid the intermediate vectors you can do something like the following:

use std::str::FromStr;

fn main() {
    let some_str = "123,321,312";
    let it0 = some_str.split(',');
    if it0.clone().count() == 3 {
        let mut it = it0.map(|s| i32::from_str(s).expect("failed to parse number"));
        let num_pair: (i32, i32, i32) =
            (it.next().unwrap(), it.next().unwrap(), it.next().unwrap());
        println!("Tuple {:?}", num_pair);
    }
}
like image 92
starblue Avatar answered Oct 14 '22 03:10

starblue


You can declare a trait with a method similar to Iterator::collect and implement it to collect to various tuples sizes:

fn main() {
    // Example with some simplifications
    // Note that there is no extra allocation
    let num_pair: (i32, i32, i32) = "123,321,312"
        .split(',')
        .map(|s| s.parse().expect("an i32"))
        .try_collect()
        .expect("a 3-tuple of i32");
    assert_eq!(num_pair, (123, 321, 312));
}

trait TryCollect<T> {
    fn try_collect(&mut self) -> Option<T>;
}

macro_rules! impl_try_collect_tuple {
    () => { };
    ($A:ident $($I:ident)*) => {
        impl_try_collect_tuple!($($I)*);

        impl<$A: Iterator> TryCollect<($A::Item, $($I::Item),*)> for $A {
            fn try_collect(&mut self) -> Option<($A::Item, $($I::Item),*)> {
                let r = (try_opt!(self.next()),
                         // hack: we need to use $I in the expasion
                         $({ let a: $I::Item = try_opt!(self.next()); a}),* );
                Some(r)
            }
        }
    }
}

macro_rules! try_opt {
    ($e:expr) => (match $e { Some(e) => e, None => return None })
}

// implement TryCollect<T> where T is a tuple with size 1, 2, .., 10
impl_try_collect_tuple!(A A A A A A A A A A);

Other examples:

fn main() {
    let mut iter = (0..7).into_iter();

    let (a, b, c) = iter.try_collect().unwrap();
    assert_eq!((a, b, c), (0, 1, 2));

    let (d, e) = iter.try_collect().unwrap();
    assert_eq!((d, e), (3, 4));

    let (f,) = iter.try_collect().unwrap();
    assert_eq!(f, 5);

    let a: Option<(u32, u32)> = iter.try_collect();
    assert_eq!(None, a);
}
like image 36
malbarbo Avatar answered Oct 14 '22 04:10

malbarbo


EDIT: A simple one-liner solution: collect_tuple of the popular itertools crate.

let iter = 1..3;
let (x, y) = iter.collect_tuple().unwrap(); // yeah!

Original answer

If you want better IDE type hinting, macros may not be a perfect solution. Here is my attempt:

fn tuple1<T>(a: &[T]) -> (&T) { (&a[0]) }
fn tuple2<T>(a: &[T]) -> (&T, &T) { (&a[0], &a[1]) }
fn tuple3<T>(a: &[T]) -> (&T, &T, &T) { (&a[0], &a[1], &a[2]) }
fn tuple4<T>(a: &[T]) -> (&T, &T, &T, &T) { (&a[0], &a[1], &a[2], &a[3]) }
fn tuple5<T>(a: &[T]) -> (&T, &T, &T, &T, &T) { (&a[0], &a[1], &a[2], &a[3], &a[4]) }
fn tuple6<T>(a: &[T]) -> (&T, &T, &T, &T, &T, &T) { (&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) }
like image 33
ch271828n Avatar answered Oct 14 '22 03:10

ch271828n