Move values from generic vector into struct

Hi,

While learning Rust I stumbled upon a problem that I don't quite understand and I thought it'd be wise to reach out for help. I did find a solution but it didn't feel right (I discuss that later on).

What I want is: to build a function that takes the ownership of a generic vector, breaks its values from it, and builds a generic Struct that has these values as coordinates. It is important to emphasize that I do not want to impose a Copy trait bound in this function.

Below we have the Struct that I am using.

#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

As an example, our vector will be made up of String elements.

fn main() {

    // Some generic vector (with possible non-Copy types)
    let v = vec![String::from("hello"), String::from("world")];

    // Take ownership of the vector and consume its values so we build a new 'point'
    let point: Point<String> = point_from_vec(v);

    println!("point = {:?}", point);  // point = Point { x: "hello", y: "world" }
}

While I did find a way to solve this problem, I have a strong feeling that:

(1) I've overlooked something (maybe simple maybe not...),

(2) there exists a much better way to approach these kind of problems,

(3) what I want to do doesn't make any sense because I should've been structuring these problems in a completely different way.

The solution I mentioned is shown below:

fn point_from_vec<T>(v: Vec<T>) -> Point<T> {
    assert_eq!(v.len(), 2);  // to ensure that the 'unwrap' at the end of the function won't fail

    // I create a couple of mutable objects that have to be 'Options' so I can initialize the 
    // types that are not yet concrete.
    let mut x: Option<T> = None;
    let mut y: Option<T> = None;

    // The idea here is to consume the vector (so I can essentially move the values out of the 
    // owned vector) and overwrite the variables 'x' and 'y'. Not an elegant way to do this I guess.
    for (i, value) in v.into_iter().enumerate() {
        if i == 0 {
            x = Some(value);
        } else {
            y = Some(value);
        }
    }

    Point { x: x.unwrap(), y: y.unwrap() }
}

If you only care the vector of length 2, you can .try_into() it into array.

let [x, y]: [_; 2] = v.try_into().unwrap(); // or handle error
Point { x, y }
4 Likes

For comparison, here's one way to do it with an iterator-based approach:

fn point_from_vec<T>(v: Vec<T>) -> Point<T> {
    let mut iter = v.into_iter();
    let x = iter.next().expect("Vector was empty");
    let y = iter.next().expect("Vector contained one element");
    assert!(iter.next().is_none(), "Vector contained more than two elements");

    Point { x, y }
}

Or popping (in a reversed iterator fashion):

fn point_from_vec<T>(mut v: Vec<T>) -> Point<T> {
    let y = v.pop().expect("Vector was empty");
    let x = v.pop().expect("Vector was contained one element");
    assert!(v.pop().is_none(), "Vector contained more than two elements");

    Point { x, y }
}

But this terse version probably optimizes better:

fn point_from_vec<T>(mut v: Vec<T>) -> Point<T> {
    assert_eq!(v.len(), 2);
    let y = v.pop().unwrap();
    let x = v.pop().unwrap();

    Point { x, y }
}
1 Like

if you don't care clone two values:

#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

fn point_from_slice<T: Clone>(slice: &[T]) -> Point<T> {
    match slice {
        [first, second] => Point {
            x: first.clone(),
            y: second.clone(),
        },
        [] | [_] | [_, _, _, ..] => panic!("we only care of slice of len 2"),
    }
}

fn main() {
    let v = vec![String::from("hello"), String::from("world")];
    let result = point_from_slice(&v);
    println!("result: {:?}", result);
}

1 Like

Thank you very much for your help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.