Use "as" will make collect() can't infer type

These code compiles:

let res: Vec<i32> = (0..=10).collect();

but this does not:

let res: Vec<i32> = (0..=10).collect() as Vec<i32>;

The compiler says:

12 |         let res: Vec<i32> = (0..=10).collect() as Vec<i32>;
   |                                      ^^^^^^^ cannot infer type for type parameter `B` declared on the associated function `collect`
   |
   = note: type must be known at this point
help: consider specifying the type argument in the method call
   |
12 |         let res: Vec<i32> = (0..=10).collect::<B>() as Vec<i32>;
   |                                             ^^^^^

error: aborting due to previous error

Can somebody help explain why?

as causes a cast from one type to another. Rust knows that you are trying to cast to a Vec<i32> but can't figure out what type you're trying to cast from.

What you need is type ascription, an unstable Rust feature that allows:

let res: Vec<i32> = (0..=10).collect(): Vec<i32>;

In the meantime you can use type inference and write Vec<i32> in the signature of the binding or use turbofish:

let res: Vec<i32> = (0..=10).collect::<Vec<i32>>();
2 Likes

So without as Vec<i32>, rust can know I'm going to collect a Vec<i32> because I specify let res: Vec<i32>; but with as Vec<i32> this information is somehow overwrite the res: Vec<i32> so rust can't know what I want to collect to, is that right?

When you write:

let foo: MyType = bar;

The compiler knows that bar's type is supposed to be MyType. As @Kestrer mentioned, there is also an unstable feature called "type ascription" to write things like:

some_generic_fn(bar: MyType);

This way, you don't need to use a let statement just to help the compiler guess the type of bar.


Back to your question, when you write:

let res: Vec<i32> = (0..=10).collect();

First, let's use X for the type of (0..=10).collect(). Here, you assign X to Vec<i32>, so the compiler knows that X should be Vec<i32>. However, when you write:

let res: Vec<i32> = (0..=10).collect() as Vec<i32>;

The compiler has no clue what X is. Here, you cast X to Vec<i32> then assign that to Vec<i32>, but any number of types could be cast to Vec<i32>, so the compiler doesn't have enough information to guess X. Or at least the compiler is trying to account for the future possibility of more types that can be cast to Vec<i32>, because if there are a number of those types, your code would break because the compiler wouldn't know which one you want unless you specify the type explicitly.

As @Kestrer also mentioned, another way to solve this particular problem is by taking advantage of the fact that the result of Iterator::collect() is luckily also a type parameter, and you can specify type parameters with a turbofish (collect::<Vec<i32>>()).

2 Likes

No. It doesn't overwrite anything. x as Vec<i32> means that x will be converted to Vec<i32>. Similarly, let y: Vec<i32> = x as Vec<i32> means that x is converted to Vec<i32> and the resulting value is assigned to y, also of type Vec<i32>. At this point the cast is merely redundant, because the type annotation on let means the type of the entire right-hand side expression, and also the type in the cast means the type of the result of the conversion, i.e. the very same expression.

So nothing overwrote anything – there's simply not any type information for x, the source expression of the cast, because it's not the value of the let binding that is being type annotated.

1 Like

Do you think the compiler should display a similar explanation on such errors?

Thanks guys!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.