Skip to content
Select theme

How to tell the compiler the type of an empty Vec

Short answer: an empty Vec has no elements to infer a type from, so the element type has to be written down: either annotate the binding (let v: Vec<i32> = Vec::new();) or use the turbofish (let v = Vec::<i32>::new();). The annotation usually reads better; the turbofish earns its place when the type has to sit inside an expression.

Each block below is a complete src/main.rs; the blocks that print run with cargo run, the failing one with cargo build. Rust 1.99.0, edition 2024. The project name doesn’t matter — nothing printed here depends on it.

Write an empty Vec and use it before anything has constrained its element type:

error[E0282]: type annotations needed for `Vec<_>`
--> src/main.rs:2:9
|
2 | let numbers = Vec::new();
| ^^^^^^^ ---------- type must be known at this point
|
help: consider giving `numbers` an explicit type, where the type for type parameter `T` is specified
|
2 | let numbers: Vec<T> = Vec::new();
| ++++++++
For more information about this error, try `rustc --explain E0282`.

Vec::new() is generic over the element type — its real signature is fn new() -> Vec<T> — and an empty Vec never says what T is. The _ in Vec<_> is the compiler pointing at the one thing it couldn’t work out. The help line is fix two below, with T standing in for the element type you actually want.

::<i32> feeds the type argument straight to the call, and the name comes from the shape:

[1, 2]

The same type, written where Rust already puts types for every other binding:

[1, 2]

Both fixes are exactly equivalent, so the choice is about reading. The annotation wins more often than not: the element type sits at the left edge where you look for it, it reads like let count: i32 = 0, and it stays readable when the type gets long — Vec<(String, usize)> in a turbofish is a mouthful. Reach for the turbofish when there’s no binding to annotate; the case you’ll meet most often is collect():

[2, 4, 6]

Iterators are a chapter of their own; the point here is only that ::<> is the same operator you already used on Vec::new().

The error only fires when nothing constrains the type. One push is a constraint, so this compiles with no annotation anywhere:

[1]

That’s why the same Vec::new() line that failed at the top of this page works here: the type was inferred from the first element, not written down. The catch is that the first push fixes the type for the whole binding — push a String and every later element must be a String too.

Next in this chapter: how to store different types in one Vec.