Beyond the &

Lobsters Hottest 新闻

摘要

Rust 语言团队负责人 Tyler Mandry 在 RustConf 2026 上发表了题为 "Beyond the &" 的演讲,详细介绍了历时一年的努力,旨在使用户自定义智能指针像内置引用一样灵活,并简化 Rust 中指针与引用的心智模型。

<p><a href="https://lobste.rs/s/wks0s9/beyond">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/10/08 13:58

# 超越 & 来源:https://lwn.net/SubscriberLink/1096028/7524dbcae1be7205/ ## \[LWN 仅限订阅者的内容\] > ### 欢迎来到 LWN.net 以下仅限订阅的内容由一位 LWN 订阅者为您提供。成千上万的订阅者依靠 LWN 获取 Linux 和自由软件社区的最佳新闻。如果您喜欢这篇文章,请考虑订阅 LWN (https://lwn.net/subscribe/)。感谢访问 LWN.net! Rust 拥有多种智能指针,既有标准库提供的,也有用户自定义的。然而,一些用内置引用可以完成的操作,用用户自定义的智能指针却无法做到。Rust 项目语言团队 (https://rust-lang.org/governance/teams/#team-lang) 负责人 Tyler Mandry 在 RustConf 2026 (https://rustconf.com/) 上发表了演讲,介绍了长期以来为改变这一现状所做的努力——让智能指针拥有与内置引用同等的灵活性。Mandry 表示,他在整个 2026 年一直与语言团队中其他几位对此感兴趣的成员一起研究这个问题,项目名称为"Beyond the &" (https://goals.rust-lang.org/2026/roadmap-beyond-the-ampersand.html)。这一过程经过了大量思考,但他们最终得到的设计应该能大幅简化指针和引用工作方式的心智模型。 为了说明这个设计所解决的问题,他展示了一个简化的 Rust 程序示例:计算某些动态内容并将其缓存到哈希映射中。该示例使用了 Rust 的 map-entry API,其中包含一个名为 `or_insert_with()` (https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html#method.or_insert_with) 的函数,该函数接受一个回调来在哈希映射条目为空时填充它。 ```rust struct RenderState { cache: HashMap<String, Arc<String>>, template: String, } fn render_page(state: &mut RenderState, name: &str) -> Arc<String> { // 使用 Entry API 可变地借用 `state.cache` let entry = state.cache.entry(name.to_string()); entry.or_insert_with(|| { // 如果条目不存在,则创建它 Arc::new(state.template.replace("$name", name)) }).clone() } ``` 这段代码在哈希映射中查找缓存键(`name`),如果存在则返回缓存值的副本;如果不存在,则通过将 `name` 替换进模板来计算它。共享状态通过内置可变引用 `&mut RenderState` 来访问。这个示例是可行的,但它无法在线程之间安全地共享。如果添加一个互斥锁来使缓存可以共享,借用检查器就会拒绝这个程序: ```rust fn render_page(state: &Mutex<RenderState>, name: &str) -> Arc<String> { let state: MutexGuard<'_, RenderState> = state.lock().unwrap(); // 借用检查器抱怨说 "state" 在此被可变借用: let entry = state.cache.entry(name.to_string()); entry.or_insert_with(|| { // ... 但在它仍被借用了的情况下,又在此处使用了它: Arc::new(state.template.replace("$name", name)) }).clone() } ``` Mandry 说,原始代码之所以有效,是因为借用检查器能够追踪到 `state.cache` 和 `state.template` 是不同的字段,因此同时访问它们是安全的。加入互斥锁之后,借用检查器只看到对 `MutexGuard` 结构体的不透明访问,无法再判断这些访问是互不干扰的——它必须考虑到可变借用可能在修改状态的同时,回调又在从中读取数据,从而导致潜在的数据竞争。 在这种情况下的一个简单修复方式是先解引用 `MutexGuard`,然后对背后的结构体重新借用: ```rust let state: &mut RenderState = &mut *state.lock().unwrap(); ``` 这会构建一个新的内置引用,就像原始代码中使用的那样,这样借用检查器就能再次看到这些访问不会相互干扰。这虽然可行,但并不直观。Mandry 表示,这样的复杂性是造成 Rust 学习曲线陡峭的原因之一。如果用户自定义的指针类型(如 `MutexGuard`)能表现得更像内置引用就好了。 [![Tyler Mandry]](https://static.lwn.net/images/2026/tyler-mandry-rustconf-small.png)(https://lwn.net/Articles/1096825) 他继续说道,指针类型无处不在,而所有这些指针类型的语义都略有不同。有些指针不能安全地解引用(裸指针),有些可以写入但不能读取(`MaybeUninit` (https://doc.rust-lang.org/std/mem/union.MaybeUninit.html))等等。这体现了 Rust 的灵活性,但也使得设计一个适用于多种指针类型的方案变得困难。 语言团队最终采纳的方案基于 Nadrieril 和 Benno Lossin 的工作,即将编译器内部的"place"概念暴露给用户代码。Place 是 Rust 中对应于 C 语言左值(lvalue)的等价概念:一个可以从中读取或写入值的位置。Place 和指针之间的区别在于,place 是一个在编译时存在的抽象表达式(例如 `state.cache`),而指针是在运行时表示 place 的一种方式。 其核心思想是为每个智能指针创建一种新的句柄类型。通常,该句柄只是对同一位置的不安全指针的一个包装。然后,编译器会自动创建句柄来表示程序中引用的 place。这使得用户代码可以在句柄类型上实现 trait,从而影响借用检查器如何处理与自定义智能指针对应的句柄,其灵活性远超现有的 `Deref` (https://doc.rust-lang.org/std/ops/trait.Deref.html) 和 `DerefMut` (https://doc.rust-lang.org/std/ops/trait.DerefMut.html) trait。 作为示例,Mandry 展示了库作者如何教导借用检查器处理通过 `NonNull` (https://doc.rust-lang.org/std/ptr/struct.NonNull.html) 指针访问的 place 的写入操作: ```rust unsafe impl<T> WritePlace for NonNullHandle<T> { const SAFE: bool = false; unsafe fn write_place(self, value: T) { unsafe { self.0.write(value) } } } ``` `WritePlace` trait 用于告知借用检查器某种特定类型的句柄(这里是引用 `NonNull` 指针的句柄)可以被写入,以及如何写入。当 `SAFE` 设置为 `false` 时,借用检查器将写入相关 place 视为不安全操作。实际的写入操作被转发给 `NonNull` 所包装的裸指针。整个 trait 实现标记为 unsafe,因为错误的 trait 实现可能导致借用检查器犯错,从而造成不健全的行为。 与之对应的 `ReadPlace` trait 编码了如何从句柄中读取数据。 更有趣的是 `ProjectPlace` 和 `BorrowPlace`。前者告诉借用检查器如何将包含结构体的 place 转换为包含其某个字段的 place。例如,当程序员编写 `state.template` 时,如何将(`MutexGuard` 的句柄)转换为 `MutexGuard<String>` 的句柄。后者告诉借用检查器如何创建一个借用给定 place 的新智能指针,其工作方式与 `&` 对内置引用所做的相同。 语言团队仍在讨论使用 `BorrowPlace` 创建新智能指针的语法。虽然可以继续使用 `&` 符号,但这可能造成混淆,也会增加类型推断的难度。有人提议改用 `@` 符号,但大家对此也不完全满意。 无论最终决定采用什么语法,`BorrowPlace` trait 都编码了借用检查器安全处理该指针类型所需的全部信息。这意味着当前的内置行为也可以通过同一机制来定义。例如,内置引用类型 `&T` 的实现如下: ```rust unsafe<'a, T> BorrowPlace<&'a T> for RefHandle<'a, T> { // 告知借用检查器,可以在同一时刻 // 对同一个 place 创建多个引用: const ACCESS: AccessKind = AccessKind::Shared; // 且借用的生命周期为 'a: type Timing = Lifetime<'a>; // 以及创建引用是一个安全操作: const SAFE: bool = true; unsafe fn borrow(self) -> &'a T { unsafe { &*self.ptr.as_ptr() } } } ``` 这为用户提供了一个如何编写行为类似引用的自定义智能指针的范例,同时也为标准库维护者提供了一个记录现有反直觉内置行为的场所。 通过为 `MutexGuard` 类似地实现句柄类型,前面的 `render_page()` 示例就能无错误地工作了。Mandry 称之为"对这个示例来说是一个小的代码改动,但却是语义上的巨大转变"。 将智能指针与编译器现有的内部机制更紧密地联系起来还有其他好处。目前,可以对引用背后的值进行模式匹配,但不能对智能指针背后的值进行——至少在不解引用指针并重新借用其背后的值的情况下是不行的。`BorrowPlace` 暴露的细节足以让编译器安全地实现这些模式匹配。 Mandry 说,语言团队在评估新特性时寻找的标准之一是可组合性:候选特性与语言现有结构的整合程度,以及该特性的多种用法之间能否良好地组合。句柄和 place"优美地组合在一起",因为这只是暴露了编译器已经理解并处理语言的一些内部细节。 尽管如此,将 place 和 handle 暴露给库代码仍处于原型阶段。Mandry 请求大家帮忙确保这个设计能适用于各种用例;他请观众查看设计文档 (https://rust-lang.github.io/beyond-refs/),并补充具有奇特语义的智能指针示例。"如果您有一个希望更深度地集成到语言中的抽象,请尝试这个设计,并告诉我们遇到的任何障碍。" 他打算完善的下一个设计部分是错误和诊断信息。理想情况下,用户不应看到任何提到新添加 trait 的错误;这些 trait 保持为内部实现,错误信息应该直接解释问题所在,就像对内置引用那样。 虽然距离成为语言的稳定特性还有很多工作要做,但 Mandry 对这一设计持乐观态度。"我希望它能让库作者们使 Rust 变得更加强大和友好。" 一位观众想知道这一设计是否也支持破坏性模式匹配(destructive pattern matching)——一种在对值进行模式匹配的同时获取其所有权、将其拆解为组成部分的组合操作。Mandry 回答说,只要开发者为相应的句柄实现了 `VariantPlace` trait,就可以支持。他说,要支持句柄类型上的所有操作,大约需要实现六到八个 trait,而 `VariantPlace` 就是其中之一。 另一位观众询问该设计是否也适用于枚举。Mandry 停顿了一下,茫然地望向远方片刻,然后缓缓地给出了一个不太确定的"是"。他进一步解释说,从枚举中投射出一个字段通常可能无法实现,但在这个方向上已经进行了一些探索性工作。例如,是否可以从 `Option` (https://doc.rust-lang.org/std/option/enum.Option.html) 中投射出一个字段,得到该字段值的 `Option`? "我不知道答案,但这是一个有趣的问题,"Mandry 说道,此时会议时间刚好用完。 \[ 感谢 Linux Foundation(LWN 的差旅赞助商)协助前往蒙特利尔参加 RustConf。\] 本文的索引条目: 会议 (https://lwn.net/Archives/ConferenceIndex/) RustConf/2026 (https://lwn.net/Archives/ConferenceIndex/#RustConf-2026)

相似文章

安全 Rust 的边界

Lobsters Hottest

TokioConf 2026 的一篇演讲/博客文章探讨了如何通过为复杂指针结构实现追踪式垃圾回收,将安全 Rust 推向极限,并分享处理循环引用与原始指针 GC 设计的技巧。

2026 年是 Rust 编译器激动人心的一年(对我来说也是)

Lobsters Hottest

一篇博客文章,介绍了为什么 2026 年是 Rust 编译器具有里程碑意义的一年:新一代 trait 求解器、Polonius 借用检查器、接近稳定化的并行前端,以及 LLVM 带来的显著性能提升,所有这些都汇聚在同一时刻。

用 Rust 重写

Hacker News Top

本文评估了2026年的‘Rewrite It In Rust’运动,讨论了现实世界中的性能提升、诸如新错误和平台支持等挑战,并提倡增量重写而非完全重写。

写给 Rust 程序员的 C 语言

Lobsters Hottest

一篇由 Rust 程序员撰写的博文,记录了其自学 C 语言过程中遇到的各种奇特行为,包括通过 <stdbool.h> 实现布尔类型、以 null 结尾的字符串与 Rust 中长度前缀的 &str 之间的对比,以及相关的内存权衡。对于从 Rust 转向 C 的开发者来说,它是一份实用的配套指南。