Rust Turbofish 的由来

turbofish

0x01 什么是 Turbofish

我们运行如下 Rust Snippet:

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);
}

不出意外,Rust 编译器一定会抛出错误

aggresss@traitx tmp % branch:[main]% cargo check
    Checking tmp v0.1.0 (/tmp)
error[E0283]: type annotations needed
    --> tmp/src/main.rs:3:9
     |
3    |     let even_numbers = numbers.into_iter().filter(|n| n % 2 == 0).collect();
     |         ^^^^^^^^^^^^                                              ------- type must be known at this point
     |
     = note: cannot satisfy `_: FromIterator<i32>`
note: required by a bound in `collect`
    --> /Users/aggresss/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:2050:19
     |
2050 |     fn collect<B: FromIterator<Self::Item>>(self) -> B
     |                   ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
help: consider giving `even_numbers` an explicit type
     |
3    |     let even_numbers: Vec<_> = numbers.into_iter().filter(|n| n % 2 == 0).collect();
     |                     ++++++++

For more information about this error, try `rustc --explain E0283`.
error: could not compile `tmp` (bin "tmp") due to 1 previous error

上面的错误可以通过两种方式解决

  • 第一种方式在 even_numbers 变量声明时声明类型:

    let even_numbers: Vec<_> = numbers.into_iter().filter(|n| n % 2 == 0).collect();
    
  • 第二种方式使用 Turbofish 语法,在泛型函数后面加入 ::<>

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

泛型在编译的过程中要被实例化,大多数情况下,具体类型是基于 Hindley-Milner 理论的类型推断,但是在一些特殊情况下,我们需要显示声明类型来帮助编译器排除歧义,这也是 Turbofish 存在的原因。

在使用泛型的过程中,其他语言也会遇到类似的问题,例如 C++ 中使用 ident<T>,或者 Golang 中使用 ident[T],而 Rust 中确使用 ident::<T>,多了两个冒号(double colon),第一次看到确实觉得有点多余,通过考古在 reddit 上看到 Anna Harren (u/deadstone) 的解释后理解了这样设计的初衷,即可以降低编译器的语法解析难度。
reddit
这是一种从语言设计者角度的 Tradeoff,所以语言的使用者多少会有一些疑问或抱怨,在 Rust 社区中确实也会时常听到一些相关的声音,但是当知道了 Turbofish 语法由来的故事后,我已经开始喜欢 ::<> 这个符号。

0x02 Turbofish 的由来

在 2015 年的时候,Anna Harren 第一次提出使用 ::<> 来辅助编译器进行类型判断,同时给它起了一个很有意思的名字 –– TURBOFISH,很快这个名字也被 Rust 官方所采纳,这个符号确实挺像一条加速的鱼,还有人做了一个页面 https://turbo.fish/。不幸的是在 2021 年 Anna Harren 因患癌症离开了这个世界,Rust 1.55.0 的 Release Note 特别提到了她,并将这个版本作为对 Anna Harren 的纪念。

twitter_turbofish
可能开源社区的魅力就在于对多样性的包容,Rust 社区争吵不断,但更容易诞生有趣的事物,毕竟这个世界有那么多有趣的灵魂。

0x03 Furthermore

有一个彩蛋发现,将 Turbofish 符号 ::<> 倒过来, 即 <>::,Rust 在 Disambiguating Function Calls 中使用 :: 作为 namespace qualifier,同时使用 <>:: 作为路径中存在类型混淆时的显示声明,例如:

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

这种方法早期被叫做 UFCS (Universal Function Call Syntax),于此同时,使用 Turbofish 也可以反向解决上面的问题:

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

从优雅对称的角度看,我还是支持 Turbofish 仍然是 Turbofish。

0x04 Reference

相关推荐

  1. 1. 面向对象由来

    2024-05-03 12:16:04       10 阅读
  2. js arguments对象由来和用法

    2024-05-03 12:16:04       26 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-03 12:16:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-03 12:16:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-03 12:16:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-03 12:16:04       18 阅读

热门阅读

  1. 产品经理的产品思维

    2024-05-03 12:16:04       9 阅读
  2. RCE学习

    RCE学习

    2024-05-03 12:16:04      13 阅读
  3. Python项目开发实战:密码学之编写加密程序

    2024-05-03 12:16:04       14 阅读
  4. 区块链 | IPFS 工作原理入门

    2024-05-03 12:16:04       10 阅读
  5. 远程为ubuntu安装teamviwer(无UI界面) - 简书

    2024-05-03 12:16:04       15 阅读
  6. 绕过Microsoft登录:安装Windows 11 23H2的两种方法

    2024-05-03 12:16:04       16 阅读
  7. ocr_class

    2024-05-03 12:16:04       9 阅读
  8. 【第5章】spring-mvc请求映射处理

    2024-05-03 12:16:04       10 阅读