What is the best way to concatenate vectors in Rust?
Asked Answered
S

6

221

Is it even possible to concatenate vectors in Rust? If so, is there an elegant way to do so? I have something like this:

let mut a = vec![1, 2, 3];
let b = vec![4, 5, 6];

for val in &b {
    a.push(val);
}

Does anyone know of a better way?

Southernmost answered 24/11, 2016 at 18:34 Comment(5)
Related, possible duplicate: What's the idiomatic way to append a slice to a vector?Eighth
The code in your question doesn't compile.Brow
Can you be more specific? Do you want to produce a vector by consuming the other two, or just have an iterator over the concatenation?Policy
What's wrong with a.extend(b)?Gustaf
@Gustaf Something not convenient with a.extend(b) is it doesn't return b, and as such ask slightly more complicated expressions in functional methods like map.Prop
L
21

I think the best method to concatenate one or more vector is this:

let first_number: Vec<usize> = Vec::from([0]);
let final_number: Vec<usize> = Vec::from([3]);
let middle_numbers: Vec<usize> = Vec::from([1,2]);

let numbers = [input_layer, middle_layers, output_layer].concat();
Lythraceous answered 5/9, 2022 at 15:11 Comment(1)
This won’t matter for integers, but for some types it may be important that slice::concat needs to clone each element.Saddlebag
N
274

The structure std::vec::Vec has method append():

fn append(&mut self, other: &mut Vec<T>)

Moves all the elements of other into Self, leaving other empty.

From your example, the following code will concatenate two vectors by mutating a and b:

fn main() {
    let mut a = vec![1, 2, 3];
    let mut b = vec![4, 5, 6];

    a.append(&mut b);

    assert_eq!(a, [1, 2, 3, 4, 5, 6]);
    assert_eq!(b, []);
}

Alternatively, you can use Extend::extend() to append all elements of something that can be turned into an iterator (like Vec) to a given vector:

let mut a = vec![1, 2, 3];
let b = vec![4, 5, 6];

a.extend(b);
assert_eq!(a, [1, 2, 3, 4, 5, 6]);
// b is moved and can't be used anymore

Note that the vector b is moved instead of emptied. If your vectors contain elements that implement Copy, you can pass an immutable reference to one vector to extend() instead in order to avoid the move. In that case the vector b is not changed:

let mut a = vec![1, 2, 3];
let b = vec![4, 5, 6];

a.extend(&b);
assert_eq!(a, [1, 2, 3, 4, 5, 6]);
assert_eq!(b, [4, 5, 6]);
Northrup answered 24/11, 2016 at 22:23 Comment(0)
B
93

I can't make it in one line. Damian Dziaduch

It is possible to do it in one line by using chain():

let c: Vec<i32> = a.into_iter().chain(b.into_iter()).collect(); // Consumed
let c: Vec<&i32> = a.iter().chain(b.iter()).collect(); // Referenced
let c: Vec<i32> = a.iter().cloned().chain(b.iter().cloned()).collect(); // Cloned
let c: Vec<i32> = a.iter().copied().chain(b.iter().copied()).collect(); // Copied

There are infinite ways.

Blackmun answered 7/6, 2019 at 8:11 Comment(7)
What is the difference between consuming, cloning and coppying? I thought that there are only references (borrowing, yes?) and clonesGlaive
@DamianDziaduch wow, that broad, you ask me to explain Rust ;) If you have experience you could understand the following: basically consume move the data so a and b are not available anymore, the result is free to do what it want. Reference just borrow both vector, so you need to keep their alive as long the result want to live. Clone and Copy are very close, the first one can be expensive, the second one should be cheap. They just use both vector as a source without need them latter, so the result become free to live as long as it want as well that a and b. Hope it's clear.Blackmun
How does the performance compare to equivalent versions of extend?Araliaceous
@Blackmun sorry what do you mean? Your reply appears at the bottom to me. And a nice result with what?Araliaceous
How is cloning cheaper than copying? How are they different?Callas
@MichaelDorst doc.rust-lang.org/std/marker/…Blackmun
@Blackmun Based on that it seems to me that cloning could be either cheaper or more expensive than copying, depending on the type. Pretty sure for i32 it should be exactly the same.Callas
S
41

Regarding the performance, slice::concat, append and extend are about the same. If you don't need the results immediately, making it a chained iterator is the fastest; if you need to collect(), it is the slowest:

#![feature(test)]

extern crate test;

use test::Bencher;

#[bench]
fn bench_concat___init__(b: &mut Bencher) {
    b.iter(|| {
        let mut x = vec![1i32; 100000];
        let mut y = vec![2i32; 100000];
    });
}

#[bench]
fn bench_concat_append(b: &mut Bencher) {
    b.iter(|| {
        let mut x = vec![1i32; 100000];
        let mut y = vec![2i32; 100000];
        x.append(&mut y)
    });
}

#[bench]
fn bench_concat_extend(b: &mut Bencher) {
    b.iter(|| {
        let mut x = vec![1i32; 100000];
        let mut y = vec![2i32; 100000];
        x.extend(y)
    });
}

#[bench]
fn bench_concat_concat(b: &mut Bencher) {
    b.iter(|| {
        let mut x = vec![1i32; 100000];
        let mut y = vec![2i32; 100000];
        [x, y].concat()
    });
}

#[bench]
fn bench_concat_iter_chain(b: &mut Bencher) {
    b.iter(|| {
        let mut x = vec![1i32; 100000];
        let mut y = vec![2i32; 100000];
        x.into_iter().chain(y.into_iter())
    });
}

#[bench]
fn bench_concat_iter_chain_collect(b: &mut Bencher) {
    b.iter(|| {
        let mut x = vec![1i32; 100000];
        let mut y = vec![2i32; 100000];
        x.into_iter().chain(y.into_iter()).collect::<Vec<i32>>()
    });
}
running 6 tests
test bench_concat___init__           ... bench:      27,261 ns/iter (+/- 3,129)
test bench_concat_append             ... bench:      52,820 ns/iter (+/- 9,243)
test bench_concat_concat             ... bench:      53,566 ns/iter (+/- 5,748)
test bench_concat_extend             ... bench:      53,920 ns/iter (+/- 7,329)
test bench_concat_iter_chain         ... bench:      26,901 ns/iter (+/- 1,306)
test bench_concat_iter_chain_collect ... bench:     190,334 ns/iter (+/- 16,107)
Simba answered 15/9, 2020 at 13:28 Comment(3)
I see chain is still broken on this... that sad. your bench_concat_iter_chain do nothing, iterator are lazyBlackmun
running this on rustc 1.55.0-nightly (885399992 2021-07-06) have much better result, houra !Blackmun
In case others had the same hiccup as me: to run the above place in a benches folder in your crate. Ensure the nightly toolchain is installed with rustup install nightly and you can run the benchmarks with cargo +nightly benchShf
L
21

I think the best method to concatenate one or more vector is this:

let first_number: Vec<usize> = Vec::from([0]);
let final_number: Vec<usize> = Vec::from([3]);
let middle_numbers: Vec<usize> = Vec::from([1,2]);

let numbers = [input_layer, middle_layers, output_layer].concat();
Lythraceous answered 5/9, 2022 at 15:11 Comment(1)
This won’t matter for integers, but for some types it may be important that slice::concat needs to clone each element.Saddlebag
T
0

made some fix on Mattia Samiolo's answer:

            let first_number: Vec<usize> = Vec::from([0]);
            let final_number: Vec<usize> = Vec::from([3]);
            let middle_numbers: Vec<usize> = Vec::from([1, 2]);

            let numbers = [first_number, middle_numbers, final_number].concat();
            println!("{:?}", numbers);
Tangible answered 10/11, 2022 at 8:9 Comment(0)
D
0

One option is to use the extend method, which allows you to append the elements of one vector to another. Like so:

let mut a = vec![1, 2, 3];
let b = vec![4, 5, 6];

a.extend(b);

This will append the elements of b to the end of a, resulting in a vector a with the elements [1, 2, 3, 4, 5, 6].

Another way is you can use the concat function from the std::iter module to concatenate two vectors. This function takes two vectors as arguments and returns a new vector that is the concatenation of the two input vectors. Like so:

use std::iter;

let a = vec![1, 2, 3];
let b = vec![4, 5, 6];

let c = iter::concat(a, b);

This will create a new vector c with the elements [1, 2, 3, 4, 5, 6].

You can also use the [..] operator to concatenate two vectors. This operator allows you to create a new vector that is the concatenation of two input vectors. Like so:

 let a = vec![1, 2, 3];
 let b = vec![4, 5, 6];

 let c = [&a[..], &b[..]].concat();

This will create a new vector c with the elements [1, 2, 3, 4, 5, 6].

Development answered 21/12, 2022 at 21:22 Comment(1)
There is no std::iter::concat function.Saddlebag

© 2022 - 2024 — McMap. All rights reserved.