7.2 KiB
10 · 错误与资源
0/16 项已确认。当前推进 10-01;本分类尚未完成选择。
本页整理原讨论的问题、验收要求和候选方案。勾选代表讨论中已确认的规则,不代表编译器或运行时已经实现。
10-01 失败的基本控制流
- 待讨论
确定使用错误结果、异常还是混合机制,以及失败后调用处如何继续。
10-02 错误的表示与分类
- 待讨论
确定错误携带哪些信息、如何区分类别和匹配处理器,是否允许自定义错误。依赖【对象与类型模型】。
10-03 失败是否属于接口契约
- 待讨论
确定函数是否声明可能的失败、调用者是否必须处理,以及遗漏处理如何检查。依赖【类型系统】。
10-04 向上传播与机制转换
- 待讨论
确定自动或显式传播规则,以及错误结果与异常之间是否允许转换、转换保留什么信息。
10-05 捕获与恢复
- 待讨论
确定处理器的匹配顺序、可见范围、重新传播,以及处理后从哪里继续;明确是否支持回到失败点继续执行。
10-06 退出作用域与栈展开
- 待讨论
确定错误跨越调用和作用域时的退出顺序,哪些清理必须执行,以及清理能访问哪些仍有效的数据。
10-07 清理结构与执行时机
- 待讨论
决定支持哪些 finally、defer 或资源作用域机制,明确注册时机、执行顺序,以及正常结束、提前返回和错误退出的行为。
10-08 资源责任与关闭规则
- 待讨论
确定谁负责关闭、责任能否转移,以及共享、重复关闭和关闭后使用如何处理。依赖【所有权与生命周期/类型系统】。
10-09 清理过程中再次失败
- 待讨论
确定原始错误与清理错误如何保留,后续清理是否继续,以及最终向外报告什么。
10-10 析构、终结与 GC 的边界
- 待讨论
确定是否提供析构或终结机制、执行保证及限制,明确内存回收能否承担资源关闭责任。依赖【内存管理与运行时】。
10-11 不可恢复失败与终止
- 待讨论
区分可处理失败、程序缺陷和强制终止;确定各类情况下是否展开和清理,以及未处理错误到达程序边界后的行为。
10-12 异步操作与取消中的清理
- 待讨论
明确是否支持异步清理、取消如何传播,以及清理期间再次取消的行为。依赖【并发与异步】。
10-13 并发中的多个失败
- 待讨论
确定任务失败由谁接收,多个失败如何组合,其他任务是否继续或取消。依赖【并发与异步】。
10-14 资源协议与外部接口边界
- 待讨论
明确语言内置保证与库协议的分工,以及外部调用的错误如何进入本语言、跨越外部调用时谁负责清理。依赖【定制协议、标准库与外部互操作】。
10-15 错误诊断与上下文保留
- 待讨论
确定是否保留调用位置、传播路径、原始原因与附加上下文,以及包装、重抛后保留什么。
10-16 跨分类一致性验收
- 待讨论
用正常返回、嵌套失败、清理失败、未处理失败及适用的取消场景核对全部规则,消除与其他分类决定的冲突和阻塞。
当前问题的方案与语言对照
以下沿用原讨论材料;建议和参考语言的做法均不自动成为本语言规则。
当前仍是 10-01:常规操作失败时,返回失败结果,还是直接中断控制流? 这里先讨论文件不存在、配置格式错误等情况,致命故障留到 10-11。
相关语言怎样做
| 语言 | 规则与小例子 | 好处、代价与启发 |
|---|---|---|
| Python | 广泛使用异常。例如 int("abc") 抛出 ValueError,跳过当前路径中的后续语句,寻找匹配的 except;中间函数不必逐层转发。也允许接口返回普通错误结果。官方文档 |
正常路径紧凑,便于外层集中处理;调用表达式本身通常看不出可能抛什么。启发:选择异常,就要接受并管理这种隐式退出路径。 |
| JS | 支持 throw 和 try/catch,可抛出任意值。例如 throw "bad config" 会中断当前路径,由外层捕获。语言规范 |
传播方便,但处理器不能天然假定拿到的是标准错误对象。启发:采用异常与允许抛什么是两个独立决定。 |
| TS | 运行时沿用 JS 的异常行为。静态检查可以约束错误的使用:启用 useUnknownInCatchVariables 时,捕获值默认为 unknown,访问其属性前需要缩小类型;这不等于静态列出函数可能抛出的错误。官方文档 |
能提高处理代码的安全性,却不会自动消除隐式异常路径。启发:静态类型检查与失败控制流要分别设计。 |
| Go | 常规错误通常作为额外返回值,例如得到 (文件, error) 后检查 err != nil;返回错误本身不会自动退出调用者。另有 panic/recover 机制。官方说明 |
检查与返回位置明确;逐层传播容易重复,也可能忽略错误。启发:显式错误值和另一种中断机制可以并存,但需要明确用途。 |
| Rust | 常规可恢复错误使用 Result<T, E>,区分 Ok 与 Err。例如 File::open(path)? 在失败时提前返回当前函数;不用 ? 时,可检查或传递这个结果。官方文档 |
结果结构清楚,传播简写减少重复;需要相应的类型与转换规则。启发:显式结果并不要求每次手写完整分支,传播语法可以另行设计。 |
这些实践提供参考,不替我们作决定;表中的好处与代价是取舍分析,不是对历史设计动机的断言。
我们当前的三个候选方案
沿用“配置文件不存在就使用默认配置,格式错误就停止启动”的例子:
- A.常规失败统一返回显式结果:调用者检查或显式传播。可以随后讨论传播简写,无须现在确定类型语法。
- B.常规失败统一通过异常传播:调用者捕获“文件不存在”来恢复,格式错误继续向外传播。
- C.两者并存:允许不同接口选择,但必须进一步确定分工,避免调用者不清楚该检查返回值还是捕获异常。
我的暂时建议是优先考虑 A:它让失败路径容易在调用处辨认,也能通过传播简写减少重复。如果你更重视多层调用后集中处理错误,B 更值得选。选择 C 则需要有明确的两类使用场景,收益才足以抵消复杂度。语言用途尚未确定,这个建议仍是候选。
你更倾向 A、B,还是 C?
讨论来源
《语言设计 10:错误与资源》,整理日期 2026-10-28。章节编号沿用原清单。