Rust Turbofish 的由来

0x01 什么是 Turbofish

我们运行如下 Rust Snippet:

rust 复制代码
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) 的解释后理解了这样设计的初衷,即可以降低编译器的语法解析难度。

这是一种从语言设计者角度的 Tradeoff,所以语言的使用者多少会有一些疑问或抱怨,在 Rust 社区中确实也会时常听到一些相关的声音,但是当知道了 Turbofish 语法由来的故事后,我已经开始喜欢 ::<> 这个符号。

0x02 Turbofish 的由来

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

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

0x03 Furthermore

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

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

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

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

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

0x04 Reference

相关推荐
凌云行者7 小时前
使用rust写一个Web服务器——单线程版本
服务器·前端·rust
cyz14100110 小时前
vue3+vite@4+ts+elementplus创建项目详解
开发语言·后端·rust
超人不怕冷11 小时前
[rust]多线程通信之通道
rust
逢生博客13 小时前
Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)
开发语言·后端·嵌入式硬件·rust
Maer0913 小时前
WSL (Linux)配置 Rust 开发调试环境
linux·运维·rust
白总Server18 小时前
CNN+Transformer在自然语言处理中的具体应用
人工智能·神经网络·缓存·自然语言处理·rust·cnn·transformer
凌云行者19 小时前
使用rust写一个Web服务器——async-std版本
服务器·前端·rust
winddevil1 天前
[rCore学习笔记 029] 动态内存分配器实现-以buddy_system_allocator源码为例
rust·嵌入式·rcore
<e^πi+1=0>1 天前
Rust Web自动化Demo
rust
allanGold1 天前
VSCode rust文件中的api点击无法跳转问题
vscode·rust