Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vec<> push & concat vs append vs extend_from_slice

Tags:

rust

I'm constructing a single Vec<f64> out of smaller Vec<f64>s and can't quite understand which is the right way to do it.

Here are three attempts with commentary on each:

fn main() {
    // this vector will be getting changed so is mutable
    let mut a:Vec<Vec<f64>> = vec![];

    // these won't be getting changed, they gets appended to `a`
    let b = vec![0.0, 1.0];
    let c = vec![2.0, 3.0];

    a.push(b);
    a.push(c);
    
    // and then the vector gets flattened into the form that I require:
    dbg!(a.concat());

    // however, if I want a single level vector constructed from other vectors
    let mut d:Vec<f64> = vec![];

    // b and c have to be mutable, why is that?
    // their contents don't change?
    // why can't they just be consumed?
    let mut e = vec![0.0, 1.0];
    let mut f = vec![2.0, 3.0];

    d.append(&mut e);
    d.append(&mut f);

    dbg!(d);

    // another method is to extend from slice
    // does this have performance problems compared to the other methods due
    // to the copying required?
    let mut g:Vec<f64> = vec![];
    
    let h = vec![0.0, 1.0];
    let i = vec![2.0, 3.0];

    g.extend_from_slice(&h);
    g.extend_from_slice(&i);

    dbg!(g);

}

Output:

[src/main.rs:15] a.concat() = [
    0.0,
    1.0,
    2.0,
    3.0,
]
[src/main.rs:28] d = [
    0.0,
    1.0,
    2.0,
    3.0,
]
[src/main.rs:46] g = [
    0.0,
    1.0,
    2.0,
    3.0,
]

I'm leaning towards extend_from_slice as it communicates to the reader of the code that vectors h & i will not be changing.

But my question is: is there a performance hit due to the copying? Is there a way to just consume the data when appending without making vectors e & f mutable?

like image 477
dim_voly Avatar asked Dec 04 '25 01:12

dim_voly


2 Answers

The difference is append will move each item from one Vec to another, extend_from_slice will clone each item.

In terms of performance between the two, in your case there is a slight difference as even though a clone and copy of a f64 is the same, append uses std::ptr::copy_nonoverlapping witch is equivalent to memcpy, and extend_from_slice iterates over and clones each item.

Where performance really matters when the source Vec contains values with dynamic memory and want to avoid the re-allocation that comes with clone (such as String) or the source Vec is large.

The reason the source to append needs to be mutable is because it is mutating its inner state by moving the values out and setting its length to 0.

I would say that concat is for a different use case where the number of source structs is unknown, and is likely to have worse performance due to it needing to iterate over all items.

like image 146
pigeonhands Avatar answered Dec 06 '25 21:12

pigeonhands


append() empties the source Vecs, so it needs to take them as &mut. It does not move them because you may still want to use their allocated memory.

In terms of performance, all methods use plain memcpy() - append() because it moves the items and the other methods because they're specialized to use a simple copy for Copy items. However, append() and extend_from_slice() grow the vector twice, for each addition, while concat() calculates the required capacity ahead of time so it is likely to be faster.

like image 41
Chayim Friedman Avatar answered Dec 06 '25 22:12

Chayim Friedman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!