< 返回版块

苦瓜小仔 发表于 2026-01-06 13:44

Tags:日报

文章《停止转发错误,开始设计错误》

核心思想: 错误处理不应只是简单的“向上抛(Forwarding)”,而应该经过“设计(Designing)”,以服务于“机器自动化恢复”和“人类排查代码”两个目标。

作者认为当前的 Rust 错误处理实践过于关注“类型如何匹配”,而忽视了错误的实际用途。

现有方案的局限性:

  • thiserror 的弊端: 倾向于按**来源(Origin)**分类(如 DatabaseError, SerdeError),但这不能告诉调用者“下一步该做什么”(该重试吗?该显示给用户吗?)。
  • anyhow 的弊端: 过于便捷,导致开发者容易跳过 .context(),使得错误向上流转 20 层后变成毫无语义的原始底层报错(如:JSON 解析失败,但不知道是哪行业务代码引发的)。
  • 堆栈追踪(Backtrace)不是万能药: 在异步代码中,堆栈追踪往往包含大量 Future 轮询噪声,且捕获开销大。

核心理念:面向受众设计

  • 面向机器(Automated Recovery): 错误需要是**扁平、可行动、按类别(Kind-based)**划分的。参考 OpenDAL 的设计:不仅有 ErrorKind(NotFound, RateLimited),还有显式的 ErrorStatus(Permanent/永续失败, Temporary/可重试失败)。
  • 面向人类(Debugging): 错误需要提供富上下文和逻辑路径
    • 减少摩擦: 使用 #[track_caller] 自动捕获源码行号,而不是全量的堆栈追踪。
    • 强制添加上下文: 在跨越模块边界时,利用类型系统强制要求添加业务语境(作者为此编写了 exn crate)。

技术方案建议:

  • 在模块边界使用类似于 exn 的方式包装错误,使得调用方如果想获取底层错误,必须显式经过一次上下文转换。
  • 避免深度嵌套的错误枚举,转向带有状态标记(如 retryable)的扁平结构。

Reddit 上的开发者们针对博文提出的激进观点进行了激辩:

关于重试逻辑的归属:

  • 一些用户质疑在错误对象中嵌入 retryable(可重试性)标志。他们认为“是否重试”应由调用方根据其自身的策略决定。
  • 作者回应:底层组件通常比调用方更了解失败的原因(例如,网络 404 与磁盘损坏)。返回一个建议性的 Permanent/Temporary 状态是协助调用方决策的最佳平衡。

关于 exn 工具和上下文格式:

  • 反对意见: 部分人不喜欢使用字符串插值来格式化错误,认为这会丢失结构化数据。
  • 替代建议: 讨论中提到了 tracing::event! 式的结构化记录,或使用 rootcausecolor-eyresnafu 等成熟库来增强可观察性。
  • 技术细节: 有用户指出 exn 似乎是在绕弯路重写还没稳定的 Rust Provider API(RFC 3192)。

开发哲学与契约设计:

  • 部分开发者推崇“设计契约(Design by Contract)”。他们认为过多的错误处理标记可能说明系统边界设计模糊。
  • 赞同者认为文章点到了痛点:许多中大型 Rust 项目的代码库中充满了一堆由 thiserror 生成的、除了解压到顶层打印之外别无他用的层叠枚举(The Onion of Enums)。

总结: 不要只做“中转站”用 ? 转发底层报错,每个模块都应为它抛出的错误定义明确的 性质 (对机器可见的状态码)和 情境 (对人可见的具体参数与操作目的)。这样在生产环境凌晨 3 点报警时,日志里显示的是 failed to fetch user_id 123 而不是一行冰冷的 serialization error

阅读:https://fast.github.io/blog/stop-forwarding-errors-start-designing-them/

rtc:Sans-I/O 的 WebRTC 协议栈

rtc 库发布了 0.3.0 版本。该项目是 webrtc-rs 组织下的核心库,旨在用纯 Rust 构建一个高性能、Sans-I/O(无 I/O 相关性) 的 WebRTC 协议栈。

项目背景与愿景:从“Go 移植”到“Rust 原生”:

  • 摆脱 Pion 的影子: 早期 Rust 的 WebRTC 实现(webrtc-rs)很大程度上是 Go 语言项目 pion/webrtc 的直接移植。这导致了许多“不符合 Rust 习惯”的设计,如大量的通道(Channels)使用和复杂的异步运行环境耦合,使得性能调优和维护非常困难。
  • 全新架构: rtc 项目(特别是 0.3.0 版)是对 WebRTC 栈的一次重新构思,完全使用 Rust 原生思维编写。

核心设计模式:Sans-I/O (无 I/O 架构)。这是该版本最大的亮点。所谓的 Sans-I/O 意味着:

  • 逻辑与网络分离: 核心协议栈(ICE, DTLS, SCTP, SRTP 等)只是一个纯粹的状态机。它不直接操作网络套接字(Sockets),不直接读取系统时间,也不持有任何具体的异步 Runtime(如 Tokio 或 async-std)。
  • 纯字节操作: 用户通过调用方法将原始字节推入状态机,并从中提取状态机产出的包。
  • 带来的优势:
    • 极高可测性: 无需网络模拟,即可在单线程中通过简单的内存缓存区测试整个协议逻辑。
    • 极致复用性: 可以在任何环境运行(Tokio、smol、WASM,甚至是 embedded/no-std 环境,因为不需要标准库的网络 API)。
    • 无锁并发: 这种模式避免了在内核态和用户态之间频繁加锁。

rtc 0.3.0 的技术重点:

  • 模块化重构: 将复杂的 WebRTC 协议拆分为独立的组件(如 rtc-ice, rtc-dtls 等),各组件保持 Sans-I/O 属性,并在 rtc 主库中组合。
  • 改进的 API 设计: 采用了更轻量级的状态同步模型,而非庞大的全局对象模型。
  • 高性能实现: 对缓冲区(Buffers)的重用进行了优化,减少内存分配(Allocations)。
  • 全功能支持: 包括但不限于交互式连接建立 (ICE)、DTLS 数据安全连接、SCTP (Data Channels) 以及 SRTP (音视频加密传输)。

社区反馈:

  • 开发者兴奋度: Reddit 用户普遍认为“Sans-I/O”是 Rust 实现网络协议的最优解,因为 WebRTC 协议本身异常复杂,解耦 I/O 能让问题定位变得清晰很多。
  • 与主流竞争: 虽然此前也有类似思想的项目(如 str0m),但 rtc 依托于 webrtc-rs 组织,拥有更好的生态位和文档维护。
  • 迁移挑战: 虽然新版本设计更先进,但从旧的类似 Go 风格的 API 迁移到这种底层状态机驱动的 API,需要开发者更深入地理解网络层工作机制。

总结: rtc 0.3.0 标志着 Rust 已经具备了目前业界最成熟、架构最优雅的 Sans-I/O WebRTC 实现。 它的发布将极大地促进云游戏、流媒体分发和去中心化实时通信项目在 Rust 生态中的落地,特别是那些对低延迟和高定制化 I/O 运行时有极高要求的场景。

仓库:https://github.com/webrtc-rs/rtc

red-apple:用 Cargo 编译输出播放

red-apple 是一个极具创意(且充满技术冷幽默)的项目,它 通过 cargo 的构建输出来播放经典动画《Bad Apple!!》。

《Bad Apple!!》在互联网文化(尤其是二次元和程序员圈子)中是一个极其特殊的图腾。它已经从一首普通的同人歌曲,演变成了衡量一台设备(或一段代码)性能与展现力的“终极跑分测试”。《Bad Apple!!》之所以成为程序员的挚爱,是因为那个黑白影绘动画有两个技术上的“神级属性”:

  1. 极简的色彩(二进制): 只有黑色和白色,这完美对应了计算机逻辑里的 0 和 1。
  2. 极高的识别度: 即便把画面压缩到非常低的分辨率,观众依然能认出里面的角色和动作。

当一个开发者说他在搞 Bad Apple 时,他通常不是在推销这首歌,而是在进行一次技术炫技。他想证明:“我看,这个系统的操控精度已经高到能用来‘播放视频’了。”

项目核心概念:

  • 不仅仅是字符画: 传统的“Bad Apple”终端项目通常是直接在终端打印 ASCII 字符。但 red-apple 的不同之处在于,它是通过 cargo build 的过程动画来呈现画面的。
  • “Cargo 为播放器”: 当你运行构建命令时,原本显示“正在编译/下载包(Compiling...)”的列表和进度条,会因为项目极其精巧的设计,排列成《Bad Apple!!》的每一帧画面。

技术实现原理:

  • 数千个虚拟包(Crate Sprawl): 该项目生成了成千上万个极小的本地 Crates。每个 Crate 的名字、版本号或目录名被精心设计成了视频中的一像素(或一组像素)。
  • 利用并行下载/编译进度: cargo 在处理依赖项时会并行更新终端行。开发者通过控制 Cargo 处理这些虚假依赖项的顺序和并行度,使终端上不断滚动的进度信息实时拼凑出了动画。
  • 生成器工具: 仓库中包含了一个处理原视频文件的 Python/Rust 脚本,它会将视频帧转化为庞大的、层级极其复杂的 Cargo 依赖关系图(Dependency Tree)。

社区反应:

  • “最 Rust 的 Bad Apple”: 网友一致公认这是将 Rust 工具链“玩坏”的最佳范例。大家笑称:“终于给高性能的并行编译找到了它最本真的用途。”
  • 对 Linker 的怜悯: 许多讨论集中在:“这得给 linker(链接器)带来多大的压力?”、“跑一次这个项目我的磁盘写入量是不是要增加几个 GB?”。
  • 性能玩笑: “如果 cargo 是在 0.1s 内完成构建的,那是动画结束了吗?还是我需要一个更慢的 CPU 来享受高帧率?”

项目意义: 这属于编程圈中典型的 “只是因为我可以(Just because I can)” 系列作品。它展示了对 cargo 构建系统内部行为的极端控制,以及 Rust 社区深厚的“黑客文化”。

一句话吐槽: 以前我们抱怨 Cargo 编译慢是在浪费生命,现在你可以说:“我不是在等编译,我是在看大片。”

仓库:https://github.com/ALinuxPerson/red-apple

--

From 日报小组 苦瓜小仔

社区学习交流平台订阅:

评论区

写评论

还没有评论

1 共 0 条评论, 1 页