Type annotation vs turbofish

Which of the following is (better|more common|preferred|idiomatic) and why?

Type Annotation

let mut other_things: Vec<_> = things.iter().filter_map(|t| {
    // ... do something with t and return Option
}).collect();

Turbofish

let mut other_things = things.iter().filter_map(|t| {
    // ... do something with t and return Option
}).collect::<Vec<_>>();

I ran into this situation today, and was just curious what other's thought.

I prefer type annotation if there’s a large scope.

To me it almost doesn't matter. Or rather, it depends. I prefer to avoid the "dangling" .collect() which feels sort of out-of-line on the last row. So in this case, I would rather write this as

let mut other_things = things
    .iter()
    .filter_map(|t| {
        // ...
    })
    .collect::<Vec<_>>();

because in this manner, there is a smaller rightward drift near the declaration, i.e. it's easier to perceive that .iter() is connected to things. The alternative would be:

let mut other_things: Vec<_> = things
    .iter()
    .filter_map(|t| {
        // ...
    })
    .collect();

but this increases said rightward drift and also moves the ugliness to the front of the statement, instead of pushing it to the end.

That said, I encounter both approaches with approximately equal frequency in ambiguous situations.

3 Likes

Yea, the formatting in my post could use a run of rustfmt. What about performance? I imagine this would yield the same machine code...I'll have to check.

I'm now curious how rustfmt deals with this :grin: Anyway, in terms of performance it should be exactly the same: the only difference between the two methods is how the type solver learns the type of other_things. From that point on, it will end up generating specialized code for Vec<Thing> anyway.

1 Like