Question
I want to initialize a vector of zeros in Rust, where the size is only known at runtime.
In C, I might write something like this:
int main(void)
{
unsigned int size = get_uchar();
int A[size][size];
memset(A, 0, size * size * sizeof(int));
}
In Rust, I tried writing a helper function like this:
fn zeros(size: u32) -> Vec<i32> {
let mut zero_vec: Vec<i32> = Vec::with_capacity(size);
for i in 0..size {
zero_vec.push(0);
}
return zero_vec;
}
I suspect the 0..size range or the argument type is causing compiler issues. This also feels more verbose than the C version. Is there a more idiomatic way to create a vector filled with zeros?
I eventually want to use it in code like this:
pub fn kmp(text: &str, pattern: &str) -> i64 {
let mut shifts = zeros(pattern.len() + 1);
}
Short Answer
By the end of this page, you will understand how to create a Vec filled with repeated values in Rust, especially when the length is only known at runtime. You will also learn why Vec::with_capacity() does not create elements, how to use the idiomatic vec![value; size] syntax, and how this applies in real code such as search algorithms.
Concept
In Rust, a Vec<T> has two important properties:
- length: how many actual elements are currently in the vector
- capacity: how much memory has been reserved
This distinction is the key idea behind the original question.
When you call:
Vec::with_capacity(size)
you only reserve memory for future elements. The vector is still empty, so its length is 0. That means Rust does not consider it to contain any usable values yet.
To create a vector that already contains size elements, all set to zero, use the idiomatic macro:
vec![0; size]
This creates a Vec with size elements, each initialized to 0.
This matters because Rust is strict about initialized memory. Unlike low-level C patterns such as memset, Rust encourages safe construction of fully initialized values. Instead of allocating raw memory and then manually zeroing it, you typically create the values directly.
For numeric types like i32, zero-filling is common and efficient. For many real programs, this pattern is used for:
Mental Model
Think of a vector like a row of labeled storage boxes.
- Capacity means you bought shelf space for 10 boxes.
- Length means how many boxes actually have items in them.
Vec::with_capacity(10) means:
- you reserved space for 10 boxes
- but none of the boxes contain an item yet
vec![0; 10] means:
- you created 10 boxes
- each box already contains
0
So if you need actual usable values right away, you want a vector with a real length, not just extra capacity.
Syntax and Examples
The most idiomatic syntax is:
let values = vec![0; size];
This creates a Vec containing size copies of 0.
Example: create a zero-filled vector
fn zeros(size: usize) -> Vec<i32> {
vec![0; size]
}
Usage:
fn main() {
let a = zeros(5);
println!("{:?}", a);
}
Output:
[0, 0, 0, 0, 0]
Why usize instead of ?
Step by Step Execution
Consider this code:
fn main() {
let size = 4;
let numbers = vec![0; size];
println!("{:?}", numbers);
}
Step by step:
sizeis set to4.vec![0; size]tells Rust to create a vector.- Rust allocates enough space for 4 elements.
- Rust fills each element with
0. numbersbecomes:
[0, 0, 0, 0]
println!prints the vector.
Now compare that to this:
let numbers: Vec<i32> = Vec::();
Real World Use Cases
Zero-filled vectors appear often in practical Rust programs.
Counting and frequency tables
let mut counts = vec![0; 26];
Useful for:
- letter frequency
- histogram buckets
- event counters
Dynamic programming
let dp = vec![0; n + 1];
Useful for:
- path counting
- coin change
- edit distance
String matching algorithms
let prefix = vec![0; pattern.len()];
Useful for:
- KMP prefix table
- failure function arrays
Buffers for processing data
let buffer = vec![; ];
Real Codebase Usage
In real Rust codebases, developers usually choose one of these patterns depending on intent.
1. Create fully initialized data
let visited = vec![false; node_count];
let distances = vec![0; node_count];
This is the standard choice when every element should exist immediately.
2. Reserve space for future pushes
let mut items = Vec::with_capacity(expected_count);
for value in source {
items.push(value);
}
This is useful when you know roughly how many items will be added, but you do not want placeholder elements.
3. Use guard clauses before allocation
fn build_table(size: usize) -> Vec<i32> {
if size == 0 {
return Vec::();
}
[; size]
}
Common Mistakes
Mistake 1: Confusing capacity with length
Broken example:
let mut v: Vec<i32> = Vec::with_capacity(5);
println!("{}", v[0]);
This fails because the vector has length 0, so index 0 does not exist.
Use this instead:
let v = vec![0; 5];
println!("{}", v[0]);
Mistake 2: Using u32 for sizes
Broken example:
fn zeros(size: u32) -> Vec<i32> {
vec![0; size]
}
This does not match what expects. Vector lengths use .
Comparisons
| Approach | What it does | Best use case | Notes |
|---|---|---|---|
vec![0; size] | Creates a vector of length size, filled with 0 | When you want initialized elements immediately | Most idiomatic for this problem |
Vec::with_capacity(size) | Reserves memory for up to size elements | When you plan to push values later | Length stays 0 |
std::iter::repeat(0).take(size).collect() | Builds a vector from repeated values | When working in iterator-heavy code | More verbose here |
[0; N] |
Cheat Sheet
// Idiomatic zero-filled vector
let v = vec![0; size];
// Helper function
fn zeros(size: usize) -> Vec<i32> {
vec![0; size]
}
// Reserve capacity only (does NOT create elements)
let mut v: Vec<i32> = Vec::with_capacity(size);
// Add elements manually after reserving capacity
for _ in 0..size {
v.push(0);
}
// len() returns usize, so helper functions should usually take usize
fn zeros(size: usize) -> Vec<i32> {
vec![0; size]
}
Key rules
- Use
vec![value; size]to create repeated values. - Use
Vec::with_capacity(size)only when you plan to later.
FAQ
How do I create a vector of zeros in Rust?
Use:
let v = vec![0; size];
This creates a vector with size elements, each set to 0.
Why doesn't Vec::with_capacity() fill the vector with zeros?
Because it only reserves memory. It increases capacity, not length. The vector is still empty until you add elements.
Should I use u32 or usize for vector sizes?
Use usize. Rust collections and indexing APIs use usize for lengths and indices.
What is the difference between [0; size] and vec![0; size]?
[0; size] creates an array, usually for compile-time known sizes. vec![0; size] creates a heap-allocated vector that can use a runtime size.
Is vec![0; size] efficient?
Yes. It is the normal and idiomatic way to create a vector filled with the same value.
Mini Project
Description
Build a small Rust utility that creates and updates a frequency table for lowercase English letters. This demonstrates how zero-filled vectors are used in real programs to store counters whose size is known only when the program runs.
Goal
Create a function that counts how many times each lowercase letter appears in a string using a zero-initialized vector.
Requirements
- Create a vector of 26 zeros to represent letter counts.
- Iterate through the input string one character at a time.
- Update the correct counter for each lowercase letter.
- Ignore characters that are not lowercase English letters.
- Print or return the final counts.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Can a Struct Extend Another Struct in Rust? Composition vs Inheritance
Learn how Rust handles struct reuse without inheritance, using composition, traits, and wrapper structs with practical examples.