0

I've been working my way through the book Rust in Action and i've seen The generic syntax Vec<T> where T is any type in my understanding but also i've seen Vec<_> which I read that infers the type based off of the contents of the vector. I was wondering what the distinction between Vec<T> and Vec<_> is because I can't tell the difference between them as they appear to do the same thing? Or their descriptions would seem to lead me to believe. Why would I use one vs the other?

Brandon Piña
  • 614
  • 1
  • 6
  • 20

2 Answers2

3

The _ is called a wildcard. It basically tells Rust to just deduce the type needed, and that's it.

For Vec<T> it's a generic it does two things, it names the type, but also is a generic parameter for any type. Since you don't know what type it can be, you use T to tell in places where it should be that type, where to get the information from (in a sense).

So _ means any type, but deduced in context (a unique type), T is a generic type, and can be many types (a new generation is done for each separate type it encounters).

ShadowMitia
  • 2,411
  • 1
  • 19
  • 24
0

Vec<T> could be defined as (the actual definition is more complex):

pub struct Vec<T> {
    buf: RawVec<T>,
    len: usize,
}

As such, it's called Vec<T> because that's how the generic struct is defined. When creating a Vec, you have to specify what the type of the elements are:

let x: Vec<u8> = vec![1, 2, 3];

Here, the type annotation isn't actually needed, since the compiler can infer it for us.

Let's say we want to obtain all of the command line arguments as a Vec. We could try:

let args = std::env::args().collect();

...but that fails with:

error[E0282]: type annotations needed
 --> src/main.rs:2:9
  |
2 |     let args = std::env::args().collect();
  |         ^^^^ consider giving `args` a type

since we didn't specify which container to collect the arguments into: .collect() can collect the iterator elements into a Vec<T>, a Box<[T]>, a LinkedList<T>, and many more. Let's specify a type:

let args: Vec<String> = std::env::args().collect();

This compiles, but we can make this a bit simpler: we can do

let args: Vec<_> = std::env::args().collect();

and get the same effect, since the _ tells the compiler to infer what the type is. Since there is only one possible type inferred by the compiler, it replaces the _ with String. Vec<_> isn't a type, it's a partial type that makes the compiler fill in the _.

smitop
  • 4,770
  • 2
  • 20
  • 53