Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's a nice way to create a new vector by consuming two vectors?

Tags:

vector

rust

I would like a nice function to create a new vector, and to communicate that the argument vectors are no longer relevant and should be destroyed, take ownership of them. I don't particularly want to have to make either of the arguments mutable (to use append, extend, or push_all and thus change the calling signature). Code (playpen link):

fn main () {
    let arg1 = vec![1, 2, 3];
    let arg2 = vec![4, 5, 6];
    let desired = consume_and_concat(arg1, arg2);
    assert_eq!(desired, vec![1, 2, 3, 4, 5, 6]);
}

fn consume_and_concat(vec1: Vec<i32>, vec2: Vec<i32>) -> Vec<i32> {
    // something nice here
}

I know there's no + for Vecs. In Ruby, I'd do vec1 + vec2 or vec1.concat(vec2) or [vec1, vec2].flatten. Is there something similarly elegant that I'm missing?

like image 905
carols10cents Avatar asked Aug 19 '15 04:08

carols10cents


3 Answers

I think you are looking for Extend::extend

fn main() {
    let mut arg1 = vec![1, 2, 3];
    let arg2 = vec![4, 5, 6];
    arg1.extend(arg2);
    assert_eq!(arg1, vec![1, 2, 3, 4, 5, 6]);
}

Here, we are able to reuse the allocated space from arg1, but there's nothing to be done efficiently with the space allocated via arg2. For an example that is more specific to your original question:

fn main() {
    let arg1 = vec![1, 2, 3];
    let arg2 = vec![4, 5, 6];
    let desired = consume_and_concat(arg1, arg2);
    assert_eq!(desired, vec![1, 2, 3, 4, 5, 6]);
}

fn consume_and_concat(mut vec1: Vec<i32>, vec2: Vec<i32>) -> Vec<i32> {
    vec1.extend(vec2);
    vec1
}
like image 51
Shepmaster Avatar answered Oct 21 '22 09:10

Shepmaster


I would like to point out that marking an argument mut in the signature is unnecessary; it's just a shortcut.

Thus, we can achieve the desired result without modifying the signature:

fn consume_and_concat(vec1: Vec<i32>, vec2: Vec<i32>) -> Vec<i32> {
    let mut vec1 = vec1;
    vec1.extend(vec2);
    vec1
}

And because we reuse vec1 internal buffer, it's nearly as efficient as it gets (for better efficiency, checking the capacity of both vec1 and vec2 might lead to better result).

Of course, the shortcut:

fn consume_and_concat(mut vec1: Vec<i32>, vec2: Vec<i32>) -> Vec<i32> {
    vec1.extend(vec2);
    vec1
}

is, well, shorter, and as you already realized, the fact that vec1 is mut does not change the type of its argument and thus does not change how to pass arguments.

like image 31
Matthieu M. Avatar answered Oct 21 '22 10:10

Matthieu M.


You do not need either of them to be mutable, though if one was you would save a bit on reallocation. With Rust, one of the nice things is that you don't have to say that it consumes it. You can tell that just from the function signature. Since it doesn't take a reference, that means that the function is taking ownership.

fn main() {
    let vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];
    let concatenated_vecs = consume_and_concat(vec1, vec2);
    for num in concatenated_vecs {
        println!("{}", num);
    }
}

fn consume_and_concat(vec1: Vec<i32>, vec2: Vec<i32>) -> Vec<i32> {
    let mut result : Vec<i32> = vec![];
    result.extend(vec1);
    result.extend(vec2);
    result
}
like image 22
skyler Avatar answered Oct 21 '22 10:10

skyler