Vec<T> in Depth

vec! Macro

The vec! macro in the standard library prelude constructs a Vec<T> inline using either a comma-separated list of elements (vec![a, b, c]) or a repeat-value expression (vec![value; count]).

The Two Syntactic Forms of vec!

Macro Form Syntax What It Does Trait Bounds on T
List form vec![e1, e2, e3] Allocates a Vec<T> of capacity 3 and moves e1, e2, e3 into it None (T: Sized)
Repeat form vec![elem; n] Evaluates elem once, allocates capacity n, clones elem n - 1 times, and moves elem into the last slot T: Clone
Empty form vec![] Expands to Vec::new() (zero heap allocation) None (T: Sized)

(Note: Just like function arguments and array literals, vec![e1, e2,] permits a trailing comma.)

How the List Form Avoids Reallocations

Rather than creating an empty Vec::new() and calling .push() repeatedly (which would trigger multiple reallocations), vec![a, b, c] places the elements into a boxed fixed-size array Box<[T; N]> and converts that single heap allocation directly into a Vec<T> with len == N and capacity == N:

fn main() {
    let items = vec!["rust", "cargo", "clippy"];

    println!(
        "items = {items:?} (len = {}, capacity = {})",
        items.len(),
        items.capacity()
    );
}
items = ["rust", "cargo", "clippy"] (len = 3, capacity = 3)

Repeat Form (vec![elem; n]) and Specialization for Zeroed Primitives

In vec![elem; n], n can be any runtime usize expression (unlike [elem; N] array repeat syntax, where N must be a compile-time const):

fn make_grid_row(width: usize, default_val: i32) -> Vec<i32> {
    vec![default_val; width]
}

fn main() {
    let row = make_grid_row(5, -1);
    let flags = vec![String::from("ready"); 3];

    println!("Row:   {row:?}");
    println!("Flags: {flags:?}");
}
Row:   [-1, -1, -1, -1, -1]
Flags: ["ready", "ready", "ready"]

OS Zero-Page Optimization for vec![0; n]

When you write vec![0_u8; n], vec![0_i32; n], or any repeat expression where the element is all-zero bits, the standard library specializes vec! to call the allocator's alloc_zeroed (calloc) path instead of allocating uninitialized memory and writing zeros in a loop. For large buffers, the operating system can back vec![0_u8; n] with virtual zero pages lazily without touching every physical page upfront.

Initializing Independent Non-Clone Elements (std::iter::repeat_with)

If T does not implement Clone, or if each slot needs a freshly constructed independent value (such as unique IDs or separate channels), use std::iter::repeat_with(|| ...).take(n).collect() or (0..n).map(|i| ...).collect() instead of vec![elem; n].