首页 > 其他分享 >What is Rust's turbofish

What is Rust's turbofish

时间:2024-03-09 12:22:49浏览次数:22  
标签:What turbofish into let numbers error fn type Rust

Have you ever heard about the “turbofish”? It is that piece of Rust syntax that looks like ::<SomeType>. In this post I will describe what it does and how to use it.

First of, if you were to write something like this:

fn main() {
    let numbers: Vec<i32> = vec![
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    ];

    let even_numbers = numbers
        .into_iter()
        .filter(|n| n % 2 == 0)
        .collect();

    println!("{:?}", even_numbers);
}

The compiler would yell at you with the following message:

$ cargo check
    Checking blog-post v0.1.0 (/Users/davidpdrsn/Desktop/blog-post)
error[E0282]: type annotations needed
 --> src/main.rs:6:9
  |
6 |     let even_numbers = numbers
  |         ^^^^^^^^^^^^
  |         |
  |         cannot infer type
  |         consider giving `even_numbers` a type

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.
error: Could not compile `blog-post`.

To learn more, run the command again with --verbose.

What this message says is that it doesn’t know what type you’re trying to “collect” your iterator into. Is it a VecHashMapHashSet, or something else that implements FromIterator?

This can be fixed in two different ways. Either by declaring the type of even_numbers when you declare the variable:

let even_numbers: Vec<i32> = ...

Or by using a turbofish:

let even_numbers = numbers
    .into_iter()
    .filter(|n| n % 2 == 0)
    .collect::<Vec<i32>>();

The ::<Vec<i32>> part is the turbofish and means “collect this iterator into a Vec<i32>”.

You can actually replace i32 with _ and let the compiler infer it.

let even_numbers = numbers
    .into_iter()
    .filter(|n| n % 2 == 0)
    .collect::<Vec<_>>();

The compiler is able to do that because it knows the iterator yields i32s.

With this change our final program looks like this:

fn main() {
    let numbers: Vec<i32> = vec![
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    ];

    let even_numbers = numbers
        .into_iter()
        .filter(|n| n % 2 == 0)
        .collect::<Vec<_>>();

    println!("{:?}", even_numbers);
}

Now the compiler is happy:

$ cargo check
    Checking blog-post v0.1.0 (/Users/davidpdrsn/Desktop/blog-post)
    Finished dev [unoptimized + debuginfo] target(s) in 0.28s

When the turbofish can be used

Lets look at another, similar example:

fn main() {
    let s = "Hello, World!";
    let string = s.into();
}

Compiling this gives us an error similar to what we had before:

$ cargo check
    Checking blog-post v0.1.0 (/Users/davidpdrsn/Desktop/blog-post)
error[E0282]: type annotations needed
 --> src/main.rs:3:9
  |
3 |     let string = s.into();
  |         ^^^^^^
  |         |
  |         cannot infer type
  |         consider giving `string` a type

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.
error: Could not compile `blog-post`.

To learn more, run the command again with --verbose.

We might assume that we fix this by using the turbofish again like so:

fn main() {
    let s = "Hello, World!";
    let string = s.into::<String>();
}

However that gives us a new error:

$ cargo check
    Checking blog-post v0.1.0 (/Users/davidpdrsn/Desktop/blog-post)
error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/main.rs:3:27
  |
3 |     let string = s.into::<String>();
  |                           ^^^^^^ unexpected type argument

error: aborting due to previous error

For more information about this error, try `rustc --explain E0107`.
error: Could not compile `blog-post`.

To learn more, run the command again with --verbose.

When I was still new to Rust this error confused me a lot. Why does ::<> work on collect but not on into? The answer is in the type signatures of those two functions.

collect looks like this:

fn collect<B>(self) -> B 

And into looks like this:

fn into(self) -> T

Notice that collect is written as fn collect<B> and into is written as just fn into.

The fact that collect has a generic type <B> is what allows you to use ::<>. Had into somehow been written as fn into<B> then you would have been able to write .into::<String>(), but since it isn’t, you can’t.

If you encountered a function like fn foo<A, B, C>() then you would be able to call it like foo::<String, i32, f32>().

The turbofish can also be applied to other things such as structs with SomeStruct::<String>::some_method(). This will work if the struct is defined as struct SomeStruct<T> { ... }.

You can almost think of the things inside ( and ) to be the “value arguments” to a function and the things inside ::< and > to be the “type arguments”.

So to fix our code from above we would have to declare the type when declaring the variable:

fn main() {
    let s = "Hello, World!";
    let string: String = s.into();
}

And again the compiler is now happy:

$ cargo check
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s

We can technically also use the fully qualified trait syntax:

fn main() {
    let s = "Hello, World!";
    let string = <&str as Into<String>>::into(&s);
}

Or optionally

fn main() {
    let s = "Hello, World!";
    let string = Into::<String>::into(s);
}

I would personally just use give s a type check declaring the variable in this case.

I recommend you check out the the book if you want to learn more about how generics work in Rust.

By the way after some digging I found this reddit comment to be the origin on the word “turbofish”.

标签:What,turbofish,into,let,numbers,error,fn,type,Rust
From: https://www.cnblogs.com/RioTian/p/18062490

相关文章

  • [Rust] Thread 2: Waiting all thread to finish using join handler
    Codefrompreviousblog:usestd::thread;usestd::time::Duration;fnmain(){thread::spawn(||{foriin1..10{println!("hinumber{}fromthespawnedthread!",i);thread::sleep(Duration::from_millis(1))......
  • [Rust] Intro Thread: 1. Thread with spawn
    Weuse spawntocreateanewthread:usestd::thread;usestd::time::Duration;fnmain(){thread::spawn(||{foriin1..10{println!("hinumber{}fromthespawnedthread!",i);thread::sleep(Duration::from......
  • RUST日常使用.md
    RUST日常使用Log日志依赖:[dependencies]log="0.4.20"env_logger="0.10.0"一般使用://使用RUST_LOG默认或环境变量env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();log::warn!("Thisisanexamplemessage.&......
  • Rust笔记(上)
    Rust笔记(上)目录Rust笔记(上)关于为什么最终还是选择了Rust作为主力语言基本数据类型所有权与移动所有权移动注意Rc与Arc:共享所有权引用共享引用可变引用生命周期省略生命周期表达式块与分号声明if与matchiflet循环break错误处理panicResult自定义错误类型结构体泛型结构体结构体自......
  • Reference management in Java and Rust, and, how faster Rust can be?
    Hi,thisisablogcomparingthereferenceinrustandjava.IreallylovejavaandIhavespendsometimelearningtheframeworklikespringandothers.AftertakingCOMP6991,Ihavegotthisthink:Howjavamanagethereferenceinmyprogram?WhycanI......
  • Rust 开发的高性能 Python 包管理工具,可替换 pip、pip-tools 和 virtualenv
    最近,我在Python潮流周刊中分享了一个超级火爆的项目,这还不到一个月,它在Github上已经拿下了8Kstar的亮眼成绩,可见其受欢迎程度极高!国内还未见有更多消息,我趁着周末把一篇官方博客翻译出来了,分享给大家。作者:@charliermarsh译者:豌豆花下猫@Python猫英文:uv:Pythonpackag......
  • Rust 登上了开源头条「GitHub 热点速览」
    抱歉!上周因为出月刊工作量比较大,所以「GitHub热点速递」暂停了一期,必须要给守着更新的读者道个歉,以后每周二的「热点速递」会按时更新,下不为例......
  • [Rust] ref keyword for borrow value
    https://doc.rust-lang.org/std/keyword.ref.htmlstructPoint{x:i32,y:i32,}fnmain(){lety:Option<Point>=Some(Point{x:100,y:200});matchy{Some(refp)=>println!("Co-ordinatesare{},{}",p.......
  • [Rust] if let & while let
    https://doc.rust-lang.org/rust-by-example/flow_control/if_let.htmlhttps://doc.rust-lang.org/rust-by-example/flow_control/while_let.html Iflet:#[test]fnsimple_option(){lettarget="rustlings";letoptional_target......
  • [Rust] Using .map_err instead of wrap which cause panic
    //Usingcatch-allerrortypeslike`Box<dynerror::Error>`isn'trecommended//forlibrarycode,wherecallersmightwanttomakedecisionsbasedonthe//errorcontent,insteadofprintingitoutorpropagatingitfurther.Here,we//defin......