Turbofish ::<> 🐠

Turbofish ::<> parse collect

In some code you will see a construct that looks like this:

parse::<i32>()

or this

collect::<Vec<&str>>()

or maybe this:

collect::<Vec<_>>()

These all use the Turbofish syntax ::<>.

When is it used?

In some cases Rust can deduce the type of the variable automatically, in other cases we explicitly have to say what is the type of a value and of a variable.

For example when we want to convert a string to a number and call parse on the string, it has to know what kind of number are we expecting. An i16, an f64?

When taking an iterator and calling collect we need to somehow tell Rust what kind of values we are expecting in the resulting vector.

We can do this in the "traditional" way of declaring the type of the variable:

let val: i32 = input.parse().unwrap();

This looks strange to me as this means that the parse method has to understand what is expected from it and do its magic based on that. A more natural looking way to write this is as follows:

let val = input.parse::<i32>().unwrap();

In this case we directly told parse that we want it to convert the string to an i32.

Another case when we have an iterator (e.g. one that split returned) and we would like to go over all the elements and collect them into a vector. We either declare the new variable as being a vector:

let parts: Vec<&str> = text.split(',').collect();

or we can use the turbofish syntax to tell our expectation to the collect method:

let parts = text.split(',').collect::<Vec<&str>>();

In the case of collecting values from an iterator we can even use underscore _ instead of the internal type and Rust will figure it out.

let parts: Vec<_> = text.split(',').collect();
let parts = text.split(',').collect::<Vec<_>>();

Full example

examples/turbofish/src/main.rs

fn main() {
    parse();
    split();
}

fn parse() {
    let input = "41";

    let val: i32 = input.parse().unwrap();
    println!("next number is {}", val+1);

    let val = input.parse::<i32>().unwrap();
    println!("next number is {}", val+1);

    // type annotation needed
    // let val = input.parse::<_>().unwrap();
    // println!("next number is {}", val+1);
}

fn split() {
    let text = "apple,banana,peach,nut";

    let parts: Vec<&str> = text.split(',').collect();
    println!("{:?}", parts);

    let parts = text.split(',').collect::<Vec<&str>>();
    println!("{:?}", parts);

    let parts: Vec<_> = text.split(',').collect();
    println!("{:?}", parts);

    let parts = text.split(',').collect::<Vec<_>>();
    println!("{:?}", parts);

    // type annotation needed
    // let parts = text.split(',').collect();
    // println!("{:?}", parts);

}

Note

Actually the idea that a function (or method) know what is expected from it and works based on the type of the expected return value is not new to me. Perl has a similar idea where functions know if we are expecting a single value (a scalar) or multiple values (a list), or maybe nothing (void) and can work according to that. Some functions even go further and know if we are expecting a boolean value, a number, or a string.

It is called context in Perl and in most cases it is rather implicit making it a bit difficult to get used to.

In Rust the closest you might get to be implicit is when you use _ as part of the type-definition, but even then Rust can deduce the expected type at compile time, and if you use the Rust analyzer in your editor or IDE then it can show you the type even earlier, at "edit-time".

Origin

See the Turbofish swimming and make sure you look at the about Turbofish page.

Related Pages

Test command line application written in Rust
Split a string into two variables
Serialize and deserialize HashMap to JSON in Rust
Rocket - access custom configuration in the routes
Hard-coded vector of Strings in Rust
Command line multi-counter with storage in JSON file

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo