在过去几年里,Rust 已经从一种只有少数专心用户使用的语言,发展成为一种被许多备受关注的项目和成功的公司所使用的知名语言。随着 Rust 用户群、社区和生态系统的不断壮大,我们需要展望未来,思考如何扩展以适应不断发展的 Rust 生态系统的需求。
最近,编译器团队分享了他们的博客文章,详细介绍了他们 2022 年的愿景,语言团队发布了一份路线图,阐述了他们对 Rust 2024 年的愿景。
在这篇博客文章中,我们 Rust 库团队将分享我们对 Rust 标准库和库生态系统未来的看法。
值得注意的是,团队的作用是协调变更并进行指导、审阅和决策。大部分实际工作由贡献者(比如你),无论是 Rust 团队内部还是外部的贡献者完成。虽然我们也经常参与设计和实现工作,但我们是以贡献者的身份进行的,就像其他人一样。
以下是我们认为重要并希望协调和指导的一些主题的(不完整)总结;我们乐见发生的事情,作为一种邀请和灵感来源。
可伸缩性
如上所述,Rust 语言、标准库和生态系统正在成长并变得更加成熟。我们需要投入精力,确保这些能够平稳地持续演进。
标准库的可演进性和错误修正
Rust 的稳定性保证使得标准库难以演进。与大多数 crate 不同,我们无法发布新的主要版本,因为那实际上相当于发布了“Rust 2.0”。所以,一旦 API 稳定下来,我们就必须永远保留它,这意味着我们在稳定任何新东西时都必须极其小心。
虽然我们在添加新 API 时非常小心,但错误仍然可能发生。根据目前的经验,如果我们能回到过去,有几件事我们会以不同的方式去做。虽然这样的情况不多,但随着时间的推移,这些错误仍然可能累积,以至于有必要建立一种机制来纠正过去的错误。
Rust 语言有版本 (editions) 的概念,以便在语言本身进行破坏性变更,而不会干扰 Rust 用户。然而,库在纠正错误方面对版本的利用非常有限。我们已经将它们用于panic!()
宏和 prelude。但是,通常来说,将版本机制用于向后不兼容的库变更极其困难,因为不同版本的 crate 可以混用,而它们都使用相同的标准库。
还存在这样的情况:添加一个新 API 可能会巧妙地破坏现有的 Rust 代码,即使不更改任何现有 API。例如,当一个类型获得了一个新方法,而该方法已经通过流行的 trait(如 itertools::Itertools
或 num::Integer
)可用时,就会发生这种情况。在标准库中添加一个方法可能导致现有方法调用解析不同,从而可能破坏代码。这通常被认为是“可接受的”破坏,但随着 Rust 用法的增长,这种破坏的影响会增加,在实践中往往使得这种破坏变得不可接受。
因此,为了保持标准库的演进,我们希望与语言团队合作,开发能够缓解这些问题的语言特性
- 基于版本的 method 消歧
- 一种修正
Range
类型的方法,使得1..2
可以是Copy
。 - 一种在不破坏现有代码的情况下移除或改进锁中毒 (lock poisoning) 的方法。
- 为库提供类似于版本机制为语言提供的通用机制。
人员与协作
保持 Rust 和生态系统可伸缩性最重要的因素是人:Rust 团队成员、生态系统中 crate 的维护者、审阅者、贡献者等等。重要的是,我们应持续改进协作方式,并尽可能方便每个人以适合自己的方式参与进来。
具体来说,我们希望致力于
- 为贡献者和审阅者提供更好、更完整的指导方针;以及
- 加强与生态系统中其他部分的互动。
使 std
不再特殊 / 增强生态系统中其他 crate 的能力
标准库使用了大量的非稳定语言特性,生态系统中其他 crate 不能(或不应该)使用这些特性。虽然这对 core
来说是不可避免的,因为它包含了与 Rust 内置类型相关的所有内容,但我们应该能够让 alloc
和 std
更少地依赖非稳定特性。也许有一天,这些库可以与生态系统中的任何其他流行 crate 没有区别。
这项工作的一大部分将是与语言团队合作,帮助推动我们需要的非稳定语言特性向可以被稳定化的状态发展。
适应不同的平台
随着 Rust 越来越受欢迎,它被用于越来越广泛的平台。Rust 标准库通过 File
和 TcpStream
等功能,在抽象化 Linux 和 Windows 等流行平台之间的一些差异方面做得还算不错,但对于那些与这些平台不同的目标(如 Wasm 或内核模块),我们做得并不理想。
例如,即使平台不支持浮点运算,core
仍然包含 f32
和 f64
;即使 File::open
在你所针对的特定平台上没有实现且总是失败,std
仍然包含它。
为了更好地支持 Rust 所使用的日益多样化的平台,我们希望与语言和编译器团队合作,使标准库更容易恰当地支持具有非常不同需求的目标,同时又不给维护者、贡献者或用户带来巨大不便
- 使将 std 移植到新平台变得更容易,对于不太流行的平台,可能允许相关代码存在于
rust-lang/rust
仓库之外。 - 一种更好的方法,允许根据平台的不同,只提供
std
的部分功能。例如,使用where Platform: Unix
限定,或类似#[cfg]
可移植性 lint 的东西。 - 一种方法,允许在支持非可移植功能的平台上使用这些功能,例如在声明只在 64 位平台运行的代码中允许
u64
和usize
之间的无失败转换。 - 使标准库更加模块化,允许在某些平台上禁用例如浮点支持或文件系统支持。
改进和添加新的 API
库团队的主要重点现在是,将来也永远是标准库的公共接口。自去年起,我们甚至成立了一个独立的团队来对 API 的变更和添加作出最终决定:库 API 团队。
Rust 特意设计了一个最小的标准库。许多常用的功能都可以在生态系统中的其他 crate 中找到,而不是标准库中。
标准库应该包含什么、不应该包含什么之间的界线在哪里,这很难界定,并且有些灵活,但有几个类别是我们最感兴趣的。
易用性
标准库中的许多添加都是非常小的,旨在提高易用性。很多时候,这些事情在某种程度上已经可能实现,只是方式不够便捷。最近的一些例子包括
abs_diff()
Path::is_symlink
iter::from_fn
NonZero*::saturating_add
虽然我们总是在某些类型和 trait 上考虑为小众功能增加已有的庞大接口的权衡,但类似这样的添加仍然定期发生。
标准化生态系统所需的一些更大的特性
随着 Rust 拓展到新的领域,标准库中包含某些特性的需求越来越多。对于那些需要一致、标准的接口的功能尤其如此。一些更大的例子包括
- 异步 trait 和函数
- 分配器 (Allocators) 和可能失败的分配 (fallible allocation)
- 错误和 panic 处理
- 可移植 SIMD
- 基准测试 (Benchmarking) 和自定义测试/基准框架
减少和改进 unsafe 代码
通过提供正确的底层 API 和抽象,我们可以极大地减少用户需要编写复杂 unsafe 代码的数量。MaybeUninit
等工具指导用户编写易于理解和验证其正确性的 unsafe 代码。更好的是,一些 API 在许多情况下可以完全消除对 unsafe 代码的需求。这包括用户出于性能原因倾向于使用 unsafe
的情况。
std::arch
std::simd
- 作用域线程 (Scoped threads)
- 更多原子原语 (atomic primitives)
- 用于数组的、具有静态长度的 '
Iterator
' - 改进
MaybeUninit
和相关方法 - 扩展
NonNull
和指针方法 - 更完整的
OsString
、Path
和CString
接口 Pin
和其他 'unsafe' 类型的文档- 文件描述符 (
OwnedFd
,AsFd
等) 和句柄 (OwnedHandle
,AsHandle
等)
改进标准库内部的实现
从历史上看,标准库的实现细节受到的关注少于其公共 API。然而,最近我们看到越来越多的贡献用于改进标准库的各个部分实现。
以下是我们特别希望看到改进的一些部分
core::fmt
以及format_args!()
和fmt::Arguments
的实现- 同步原语,如
Mutex
、RwLock
和Condvar
- 清理
std::sys
中特定于平台的代码 - 尽可能避免分配,例如在调用
std::fs
中的函数时 - 使广泛使用的类型(如
std::io::Error
)更轻量级 - 清理所有不必要的
SeqCst
内存顺序 - 优化线程局部变量 (thread local variables)
结论
我们希望这份总结能带来健康的启发和激动,并让你了解库团队正在前进的方向。如果你想帮忙,无论是想做实现工作、设计、文档、组织,还是任何其他有益的工作,我们都热忱邀请你参与进来!