Question

How to Concatenate Vectors in Rust

rustvectorconcatenation

Question

I want to combine two vectors in Rust into a single vector.

For example, I have:

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

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

Is concatenating vectors in Rust possible in a more elegant or idiomatic way? If so, what is the best approach?

Short Answer

By the end of this page, you will understand how to concatenate vectors in Rust, when to use append, extend, or iterator-based approaches, and how Rust's ownership and borrowing rules affect each option.

Concept

In Rust, concatenating vectors means taking elements from one collection and adding them to another. This is very common when building lists of results, combining data from multiple sources, or preparing values for later processing.

The main thing that makes Rust slightly different from some other languages is ownership. When you combine vectors, Rust needs to know:

  • Are you moving elements from one vector into another?
  • Are you copying elements?
  • Do you want to keep the original vector intact?

Because of that, Rust provides multiple good solutions, each useful in a different situation.

The main options

  • a.append(&mut b)

    • Moves all elements from b into a
    • b becomes empty afterward
    • Usually the most efficient choice when you no longer need b
  • a.extend(b)

    • Consumes b and moves its elements into a
    • b cannot be used afterward
    • Clean and idiomatic when ownership transfer is fine
  • a.extend(&b) or a.extend(b.iter().cloned())

    • Copies or clones elements from b
    • b stays usable
    • Needed when you must keep the original vector

Your original loop works in principle for copyable values like integers, but there are more idiomatic ways to express the same intent.

Why this matters

In real Rust programs, choosing the right method affects:

  • Performance: moving values is often cheaper than cloning
  • Correctness: ownership rules prevent invalid memory access
  • Readability: idiomatic collection operations are easier to understand than manual loops

So the best way depends on whether you want to preserve the second vector and whether its element type is cheap to copy.

Mental Model

Think of a vector like a box holding items.

  • append is like pouring all items from box B into box A. Box B ends up empty.
  • extend(b) is like handing box B over so its items can be transferred into box A. You give up B.
  • extend(&b) or cloning is like making copies of the items in box B and placing the copies into box A. Box B stays unchanged.

The important question is: Do you want to move the original items, or keep them and make copies?

That one decision usually tells you which method to choose.

Take Quiz

Syntax and Examples

Core syntax

1. Move all elements and empty the second vector

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

a.append(&mut b);

println!("a = {:?}", a); // [1, 2, 3, 4, 5, 6]
println!("b = {:?}", b); // []

Use this when:

  • you no longer need b
  • you want an efficient in-place operation

2. Consume the second vector with extend

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

a.extend(b);

println!("a = {:?}", a); // [1, 2, 3, 4, 5, 6]

Use this when:

  • you are fine giving ownership of b to a
  • you do not need to use b later

3. Keep the second vector by copying values

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

a.extend(&b);

println!("a = {:?}", a); // [1, 2, 3, 4, 5, 6]
println!("b = {:?}", b); // [4, 5, 6]

This works nicely for types like integers because they implement Copy.

4. Keep the second vector by cloning values

For non-Copy types such as String, clone the items:

let mut a = vec![String::from("a"), String::from("b")];
let b = vec![String::from("c"), String::from("d")];

a.extend(b.iter().cloned());

println!("a = {:?}", a);
println!("b = {:?}", b);

5. Build a new vector from both

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

let combined: Vec<_> = a.into_iter().chain(b.into_iter()).collect();

println!("{:?}", combined); // [1, 2, 3, 4, 5, 6]

Use this when:

  • you want a brand new vector
  • you do not need the original vectors afterward

Why your original loop needs care

This version works for integers:

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

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

val is &i32, but push needs i32, so *val dereferences the reference. This works because i32 is Copy.

For types like String, that pattern will not work unless you clone.

Step by Step Execution

Consider this example:

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

a.append(&mut b);

Step-by-step

  1. a is created as [1, 2, 3].
  2. b is created as [4, 5, 6].
  3. a.append(&mut b) is called.
  4. Rust takes all elements from b and moves them into a.
  5. After the call:
    • a becomes [1, 2, 3, 4, 5, 6]
    • b becomes []

Another example with extend

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

a.extend(b);

Execution:

  1. a starts as [1, 2, 3].
  2. b starts as [4, 5, 6].
  3. extend takes ownership of b.
  4. The elements from b are moved into a.
  5. a becomes [1, 2, 3, 4, 5, 6].
  6. b is no longer available because it was moved.

Example preserving b

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

a.extend(&b);

Execution:

  1. a starts as [1, 2, 3].
  2. b starts as [4, 5, 6].
  3. &b gives a shared reference to the vector.
  4. extend reads each element from b.
  5. Because i32 is Copy, values are copied into a.
  6. a becomes [1, 2, 3, 4, 5, 6].
  7. b stays [4, 5, 6].

Real World Use Cases

Concatenating vectors shows up often in Rust applications.

Combining API results

If you fetch paginated results from an API, each page may return a Vec<Item>. You often append each page into one final vector.

all_items.append(&mut page_items);

Collecting validation errors

A form validator may gather errors from several checks and combine them into one list.

errors.extend(name_errors);
errors.extend(email_errors);

Merging log entries

A program might collect log entries from different modules and merge them into one ordered list for output or storage.

Processing files in batches

If several worker functions each return Vec<PathBuf> or Vec<String>, you can combine them into one vector for later processing.

Building command-line output

A CLI tool may gather results from multiple filters and combine them before printing or exporting.

Take Quiz

Real Codebase Usage

In real Rust codebases, developers usually choose between append, extend, and iterator chains based on ownership and performance.

Common patterns

In-place accumulation

A very common pattern is to create one result vector and keep extending it:

let mut results = Vec::new();

results.extend(source_one());
results.extend(source_two());

This is simple and readable.

Efficient draining of temporary vectors

If a helper function returns a temporary vector that is only needed once, append is useful:

let mut all = Vec::new();
let mut batch = load_batch();
all.append(&mut batch);

This avoids unnecessary cloning.

Preserving inputs

If the original data must still be used later, developers often clone or copy only when needed:

let mut selected = base.clone();
selected.extend(extra.iter().cloned());

Iterator pipelines

For transformations and merging in one flow, iterator chains are common:

let combined: Vec<_> = first
    .into_iter()
    .chain(second)
    .filter(|x| *x > 0)
    .collect();

This style is common when data is being merged and processed together.

Related patterns

  • Guard clauses: return early if one vector is empty
  • Validation: combine lists of validation errors
  • Error handling: collect warnings from multiple operations
  • Configuration merging: combine default values and user-provided values

Example guard clause:

fn merge_if_needed(mut a: Vec<i32>, mut b: Vec<i32>) -> Vec<i32> {
    if b.is_empty() {
        return a;
    }

    a.append(&mut b);
    a
}

Common Mistakes

1. Pushing references instead of values

Broken code:

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

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

Why it fails:

  • val is &i32
  • a.push(...) expects i32

Fix:

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

Or more idiomatically:

a.extend(&b);

2. Using append without making the second vector mutable

Broken code:

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

a.append(&mut b);

Why it fails:

  • append needs &mut Vec<T>
  • b must be mutable

Fix:

let mut b = vec![4, 5, 6];
a.append(&mut b);

3. Expecting b to remain unchanged after append

Broken assumption:

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

a.append(&mut b);
println!("{:?}", b); // not [4, 5, 6]

After append, b is empty.

4. Moving a vector and then trying to use it again

Broken code:

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

a.extend(b);
println!("{:?}", b);

Why it fails:

  • b was moved into extend

Fix:

a.extend(&b);
println!("{:?}", b);

5. Forgetting that non-Copy types need cloning if preserved

Broken code:

let mut a = vec![String::from("a")];
let b = vec![String::from("b")];

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

Why it fails:

  • String is not Copy
  • you cannot move out of a shared reference

Fix:

a.extend(b.iter().cloned());

6. Choosing cloning when moving would be better

Beginners sometimes clone unnecessarily:

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

a.extend(b.clone());

This works, but it may do extra work. If you do not need b later, prefer:

a.extend(b);

Comparisons

ApproachKeeps second vector?Moves values?Requires Clone/Copy?Typical use
a.append(&mut b)Yes, but empties itYesNoFast in-place merge when b can become empty
a.extend(b)NoYesNoClean merge when b is no longer needed
a.extend(&b)YesNo, copies from referencesT: Copy in practice for owned Vec<T> resultGood for simple copyable types like integers
a.extend(b.iter().cloned())YesNo, clonesYes, T: CloneKeep b for non-Copy types
a.into_iter().chain(b).collect()NoYesNoBuild a new vector from two old ones

append vs extend

  • Use append when you have a mutable second vector and want to transfer everything efficiently.
  • Use extend when you want a more general way to add items from any iterable source.

Manual loop vs collection methods

StyleProsCons
Manual for loopExplicit and easy to traceMore verbose
extendIdiomatic and conciseRequires understanding iterators/ownership
appendEfficient and clear for full-vector movesEmpties the source vector
Take Quiz

Cheat Sheet

// Move all items from b into a; b becomes empty
let mut a = vec![1, 2];
let mut b = vec![3, 4];
a.append(&mut b);

// Move items from b into a; b is consumed
let mut a = vec![1, 2];
let b = vec![3, 4];
a.extend(b);

// Keep b and copy values (good for Copy types like i32)
let mut a = vec![1, 2];
let b = vec![3, 4];
a.extend(&b);

// Keep b and clone values (good for String and other Clone types)
let mut a = vec![String::from("x")];
let b = vec![String::from("y")];
a.extend(b.iter().cloned());

// Build a new vector from two vectors
let a = vec![1, 2];
let b = vec![3, 4];
let c: Vec<_> = a.into_iter().chain(b.into_iter()).collect();

Quick rules

  • Use append when you want b emptied.
  • Use extend(b) when you do not need b anymore.
  • Use extend(&b) for Copy types if b must stay available.
  • Use iter().cloned() for non-Copy types you want to preserve.
  • Prefer moving over cloning when possible.

Edge cases

  • append requires b to be mut.
  • After extend(b), b cannot be used.
  • String is not Copy, so dereferencing &String does not work.
  • A manual loop is valid, but extend is usually more idiomatic.

FAQ

Can you concatenate vectors in Rust?

Yes. Common idiomatic ways include append, extend, and iterator chaining with chain(...).collect().

What is the most idiomatic way to combine two vectors in Rust?

If you want to add all elements from one vector into another, extend or append is usually the most idiomatic choice. The best one depends on whether you want to keep the second vector.

What is the difference between append and extend in Rust?

append takes &mut Vec<T> and empties the source vector. extend accepts any iterable and is more general.

Does append copy elements in Rust?

No. It moves the elements from the second vector into the first.

How do I concatenate vectors without losing the second vector?

For Copy types, use a.extend(&b). For non-Copy types, use a.extend(b.iter().cloned()).

Why does a.push(val) fail in a loop over &b?

Because val is a reference like &i32, but push expects an owned value like i32. For copyable values, use *val or extend(&b).

How do I concatenate vectors of String in Rust?

If you want to move them, use append or extend(b). If you want to keep the original vector, use b.iter().cloned().

Is a manual for loop wrong?

No. It is valid Rust. But extend or append is usually shorter and clearer.

Take Quiz

Mini Project

Description

Create a small Rust program that combines task lists from two sources. This demonstrates how to merge vectors while choosing whether to move data or preserve the original values. It mirrors a common real-world problem: combining data from multiple modules or API responses.

Goal

Build a Rust program that merges two vectors of tasks and prints the final combined list.

Requirements

  1. Create one vector of existing tasks.
  2. Create a second vector of incoming tasks.
  3. Combine the two vectors into one final list.
  4. Print the merged result.
  5. Show at least one approach that keeps the second vector unchanged.
Take Quiz

Keep learning

Related questions

View more