编程语言研究书库

错误处理 — 枚举、联合与错误并集

错误处理 — 枚举、联合与错误并集

这一章是全书设计哲学的正主。 前面所有「透明」「把错误往早挪」的铺垫, 在这里收成一套机制:Zig 没有异常——一个函数可能失败,这件事写在它的返回类型里; 错误是值,可以列举、可以匹配、可以传递;try 一行传播,catch 就地兜底, errdefer 专管「出错时的清理」;出错后,错误返回追踪把错误走过的每一站打印给你。 为了讲透这套机制,本章先补三块地基:枚举、裸联合、带标签联合。

1. 这一章讲什么

链条位置:第 5 章的 divide 用 ?i32 表达「可能失败」,本章把它升级成完整的错误系统。 这是 Zig 区别于 C(返回值约定,人人可无视)和异常系语言(失败藏在调用栈的暗处) 的标志性设计,也是第 1 章「错误必须出现在函数签名里」那张安全网的兑现章。

2. 顶层全景

  地基:enum(可穷举的命名值,与 switch 穷举检查互补)
        union(一个变量,几种形态,但自己记不住现在是哪种)
        tagged union(union + enum 标签:类型帮你记住现在是哪种)
  正主:error{...} 错误集(一个函数可能出的所有错,写成一个集合)
        ErrorSet!T 错误并集(返回值类型 = 一个值 或 一个错误)
        try / catch / errdefer(三种处置姿势)
        error return trace(错误的「行程单」)

  图说:地基三件套解决「怎么表达几种可能」;正主解决「失败怎么成为类型的一等公民」。

主线例子(原书自带,照走):四层调用链 main → topFunction → midFunction → bottomFunction,最底层 return error.FileNotFound,看错误怎么一步步传上来、 trace 怎么记录每一站;配一个 allocateResource 走查 errdefer 的「出错才清理」。

3. 地基一:枚举(enum)

3.1 解决什么问题

魔数(magic number) 之病:代码里到处散落 if status == 200、return -1, 数字的含义只在写代码的人脑子里。枚举给一组相关的值起名字1:

  const Day = enum { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };

枚举成员底层从 0 开始的整数,用内建函数 @intFromEnum 取出来: Day.Wednesday 是 day number 2(从 0 数)2。也可以显式指定底层值, 比如 HTTP 状态码:OK = 200, NotFound = 404, InternalServerError = 5003—— 读者不用再背数字。

3.2 枚举的四件扩展(照原书的顺序收)

  1. 方法:枚举可以带函数。TrafficLight 枚举挂一个 isSafeToGo(self), 判断 self == TrafficLight.Green4;
  2. 与 switch 配对:对枚举 switch,Zig 的穷举检查强制你处理每个成员—— 漏一个编译错误。这是对第 4 章「穷举」性质的兑现:枚举越界新增,switch 跟着报错5;
  3. 枚举字面量:const day: Day = .Saturday;——类型已知时,成员名前的 Day. 可以省,一个点号代替6。注意:省略有前提(类型已知); 没有类型标注的 .Friday 只是一个「枚举字面量」,还不属于任何枚举7;
  4. 非穷举枚举(向前兼容):普通枚举 + switch 穷举,有个工程矛盾—— 库的枚举将来新增成员,你代码里穷举的 switch 就编译不过了8。 解法是声明时带整数标签类型 + 一个 _ 占位:enum(u16) { Success = 0, Failure = 1, _ }。 _ 告诉编译器「还有未知值会出现」,于是 switch 里必须写 _ => 兜底分支9。

3.3 运行时内省

两个内建函数,本章立名字:@tagName 返回枚举值的字符串名字 (Day.Tuesday → "Tuesday")10;@typeInfo 在编译期返回类型的完整结构, 可以枚举出一个类型的所有字段11。这是 comptime 反射的入口,第 12 章还会见到。

判断(我们的,不是书里的): 「非穷举枚举 + _ 占位」是本章最容易被略过、 但对库作者最重要的一节。它回答的是一个真实的工程问题:穷举检查在「库的演化」 面前是双刃剑——它保护调用方不漏情况,也把库的任何新增变成调用方的编译错误。 Zig 的选择是把这个矛盾显式化:默认穷举(安全),要演化就声明非穷举(自觉放弃部分检查)。 这和第 1 章的处方一字不差:没有免费的默认值,选择摆在明处。 如果错,会错在: 如果团队里没人记得「这个枚举声明了非穷举」, _ 兜底分支可能写成空操作,于是「前向兼容」退化成「静默吞掉新错误码」—— 安全网仍在,但兜住的东西没人看。

4. 地基二:联合(union)

4.1 裸联合:一个变量,几种形态

联合是一种「同一时刻只装得下一种东西、但可以是好几种之一」的类型12:

  const Data = union {
      intValue: i32,
      floatValue: f64,
      textValue: []const u8,
  };

  var inputData = Data{ .intValue = 42 };          // 此刻装着整数
  inputData = Data{ .floatValue = 3.14 };          // 整体重赋,改装浮点
  inputData = Data{ .textValue = "Hello, Zig!" };  // 再改装字符串

和内层的 struct(结构体:所有字段同时存在)正相反:union 的所有字段共享同一块内存, 所以同时只能激活一个。省内存,代价是:语言不知道现在激活的是哪个。 访问没激活的字段,运行期 panic,报错原文照引: access of union field 'floatValue' while field 'intValue' is active13。 要换激活字段,必须整体重赋一个新的 union 值14。

4.2 带标签联合(tagged union):union + enum

裸联合的「不知道激活了谁」正是第 1 章要消灭的「不透明」。 Zig 的解:给 union 配一个 enum 标签,类型自己记住现在是哪个字段15:

  const Result = union(enum) {
      Success: i32,
      Error: []const u8,
  };

  fn calculate(input: i32) Result {
      if (input >= 0)
          return Result{ .Success = input * 2 }       // 非负:成功,值翻倍
      else
          return Result{ .Error = "Input must be non-negative" };
  }

  // 使用:对标签 switch,Zig 穷举检查逼你处理每一种
  switch (calculate(-5)) {
      .Success => |value|  print("Calculation result: {d}\n", .{value}),
      .Error   => |errMsg| print("Error: {s}\n", .{errMsg}),
  }

calculate(-5) → 返回 Error 分支,switch 匹配 .Error,解包出字符串16。 三个操作细节(原书都给了):switch 的分支捕获写法 |value| 拿到载荷; 要改载荷,用 |*value| 拿到可变指针再 value.* += 517; union 也能挂方法(用 @tagName(self) 判断激活字段)18; 字面量可以省略类型名,靠推断:const num = .{ .intValue = 256 };19。

带标签联合就是「第 4 章 optionals 的 generalized 版」:?i32 本质上等价于 union(enum) { value: i32, none }。这个观察原书没明说,但解释了为什么 Zig 的类型工具感觉是一套东西的不同切面(补充,来自通用知识)。

5. 正主:错误集与错误并集

5.1 错误集(error set):把「可能出哪些错」写成类型

  const NetworkError = error{
      ConnectionLost,
      Timeout,
      InvalidResponse,
  };

错误集是专为错误准备的枚举:列出这个领域可能发生的所有失败20。 它的价值不在运行时,在签名:读到 NetworkError!void 的人, 确切知道 connectToServer 会出哪几种错——「一种一个不准、量身定制」, 而不是所有函数共享一个含糊的 Exception21。

5.2 错误并集(error union):返回值或错误

把错误集和一个普通类型用 ! 拼起来,就是错误并集:

  fn parseFloat(input: []const u8) ParseError!f64 { ... }

读法:这个函数返回一个 f64,或者返回 ParseError 里的一个错误。 ![]const u8 前不写错误集名时,是「推断错误集」——编译器替你把函数体里 可能出现的错误收集起来22。

那个 ! 值得专门一记(原书有专门的注释框):在 Zig 里,! 不是「逻辑非」, 在函数签名里它专表「可能返回错误」23。这是全书出镜率最高的符号之一。

5.3 处置姿势一:try——「失败就替我把错误传上去」

  pub fn main() !void {
      const data = try fetchData("https://example.com");  // 此 URL 为原书示例(我们查阅于 2026-10-19,仅作占位)
      ...
  }

try 的语义一句话:成功就把值解开给我;失败就 return 这个错误,替我结束当前函数24。 它是「传播」的标准姿势——和第 5 章主线例子里的错误链配合,看主线例子的第一步:

  bottomFunction: return error.FileNotFound       ← 错误诞生
      ↑ try midFunction  失败 → return error.FileNotFound
          ↑ try topFunction  失败 → return error.FileNotFound
              ↑ try main  失败 → 程序以错误退出

  图说:try 是「逐层上交」的语法糖,每一层只写一词,不写 if、不写 return。

5.4 处置姿势二:catch——就地处理,或给个默认值

  // 就地处理
  _ = fetchData("https://example.com") catch |err| {  // 此 URL 为原书示例(我们查阅于 2026-10-19,仅作占位)
      print("Failed: {s}\n", .{@errorName(err)});
  };

  // 或给默认值(失败就当 0.0)
  const value = std.fmt.parseFloat(f64, "not a number") catch 0.0;

catch 接住错误,接住的错误对象用 |err| 命名,@errorName(err) 打出错误名25。 想丢弃返回值时,前面要加 _ =(Zig 不允许忽略返回值——又一个「不许假装没看见」)。

错误集可以相套:小错误集能自动升级成大错误集—— NetworkError 是 GeneralError 的子集,所以 GeneralError!void 的函数 可以直接 try 一个 NetworkError!void 的调用26。这让「底层具体、上层笼统」 的层次设计成立。

黑暗面(连着第 5 章):可以确定不会失败时,可以写 catch unreachable—— 失败就 panic(Debug)或未定义行为(Release)27。判据和 unreachable 一样: 「被别处保证」,不是「我觉得」。

5.5 处置姿势三:errdefer——只在出错时执行的清理

第 5 章的 defer 是「出作用域必执行」;errdefer 是**「出作用域且带着错误时才执行」**28。 主线例子走查(分配内存的经典场景):

  fn allocateResource(allocator: *std.mem.Allocator) !*u8 {
      const buffer = try allocator.alloc(u8, 1024);   // ① 申请 1024 字节
      errdefer allocator.free(buffer);                // ② 登记:出错就释放

      performOperation(buffer) catch |err| {          // ③ 后续操作可能失败
          return err;                                  //    失败 → 触发 errdefer
      };
      return buffer;                                   // ④ 成功 → errdefer 不执行
  }
路径执行到哪errdefer 是否执行
performOperation 成功④ 返回 buffer否——内存归调用方
performOperation 失败③ return err是——buffer 被释放,不泄漏

把「申请」和「出错才回收」在源码里写成相邻两行,泄漏从「可能性」变成 「结构上不可能」:只要申请成功,无论后面怎么失败,释放都已登记。 这就是 defer 家族和第 1 章「透明」的合奏:资源的生命周期从控制流里凸出来, 不用读者在脑内模拟所有返回路径。

6. 错误返回追踪:错误的行程单

6.1 它解决什么

传统栈追踪(stack trace)告诉你爆炸发生在哪,不告诉你炸弹怎么运到的。 Zig 的 error return trace 记录的是后者:错误沿调用链经过的每一站29。

用主线例子看产出。代码:

  pub fn main() !void          { try topFunction(); }
  fn topFunction() !void       { try midFunction(); }
  fn midFunction() !void       { try bottomFunction(); }
  fn bottomFunction() !void    { return error.FileNotFound; }

程序以错误退出时,你看到的是一份「叙述」而不是一个「地址」:

  Error: FileNotFound
  in function bottomFunction
    called by midFunction
    called by topFunction
    called by main

每一站都留名30。排障时你不用猜「是谁传上来的」,直接从诞生点 (bottomFunction 里那行 return)开始查。

6.2 开关在哪

和第 1 章的四档表完全同构:Debug / ReleaseSafe 默认开启;ReleaseFast / ReleaseSmall 默认关闭(省性能)31。没有 trace 的构建里,错误处理照常工作, 只是少了行程单;anyerror(全局错误集,所有错误的并集)在那种情况下仍可兜底32。

判断(我们的,不是书里的): error return trace 是本章最被低估的机制。 异常系语言的栈追踪在「错误对象被创建的那一刻」最详细,而 Zig 的错误是值、 沿 return 手动传播——每过一层都是一次「显式的交接」,记录交接成本极低、信息极准。 这是「没有异常」这个选择换来的红利:传播路径上没有隐形的栈展开, 所以每一步都看得见。它反过来也说明了 try 的设计为什么值: 因为传播是显式语法,trace 才能精确到词。 如果错,会错在: 如果某段代码用 catch 接住错误又返回另一个错误, trace 记录的是新错误的行程,旧错误的来路会断——「改宗」错误时, 靠 trace 是查不到最初根源的,需要日志或手动把原错误附带上。

7. 作者的判断与证据

可核的: 全部语法和报错文本(裸联合访问 inactive 字段的 panic 原文13、 ! 的签名语义23、try/catch/errdefer 的行为2428)可直接跑代码验证; 错误集子集→超集的自动升级26;trace 的默认开启档位31。

立场: 「错误值是你的 accountability partner(问责伙伴)——不许你假装没事」33; 「错误集让失败模式清晰、结构化、专业化,没有含混的错误码」34。 这两句是作者对 Zig 错误模型的价值宣言,同时也是对异常这套做法的批评 (失败藏在调用栈暗处、签名里看不见)。宣言成立,但注意它略过了成本: 每一个可能失败的调用点都要做出处置选择(try / catch / 忽略报错), 在频繁改动的原型代码里,这是真实的心理负担——C 的「先不管返回值」在 Zig 里编译都过不了。

8. 最佳实践(原书四条)与我们的增补

原书四条35:

  1. 用显式的、具体的错误类型——别造一个笼统的 error.Failed 万物兜底;
  2. defer 管常规清理,errdefer 管出错清理——分工明确;
  3. catch 优雅兜底,try 可预期地传播——每个调用点二选一,别发明第三种;
  4. 用带标签联合给错误处理加结构——多形态结果用 tag 表达。

我们的增补(不在书里):第 3 条有个没写明的推论——传播链别太长。 try 让传播零成本,代价是错误可能在六七层之后才第一次被 catch, trace 再长也要人来读。经验法则:跨「模块边界」时落一次 catch, 模块内部放心 try 传(补充,来自通用工程经验)。

9. 边界与局限

缺什么说明
错误集的并集/交集语法没讲实际写库时常用 `error{A}
trace 和 catch 的交互(改宗断链)没讲见 §6 判断块——实际排障最常踩的坑,原书未提
try 在 defer/errdefer 里的行为没展开defer 里能不能 try、errdefer 和 try 叠加的登记顺序,留给读者自己试
自定义错误携带数据没有Zig 的惯例是「错误 + 上下文分开传」或带标签联合,本章只用字符串消息示例,惯例没总结
31 道章末题是全书面之最题量远超其他章36,但填空选择题为主,只有「为什么 Zig 不许忽略错误」这类关键问题(第 30/31 题其实是下一章的广告)没有对应的深度题
和 optionals 的配合语焉不详try 不能用于 ?T(orelse try 或 `if (x)

9.5 加深一层:原书一句带过、但值得摊开的三件事

这一节是我们补的台阶(标注来源),原书在这些地方只用了暗示。

9.5.1 异常到底差在哪(先讲清对手)

「Zig 没有异常」要听懂,得先知道异常是什么。异常(exception)是异常系语言 (C++、Java、Python、C#…)的失败机制:失败时「抛出」一个异常对象, 运行时在调用栈上逆向逐层弹出(行话叫栈展开,stack unwinding), 直到有人「接住」(try/catch 块)。问题在于:

异常Zig 错误值
签名里看得见吗看不见(除非额外标注)!T 明写
传播方式运行时栈展开,隐式try 逐层显式上交
失败路径的记录栈追踪只到抛出点error return trace 记录每一站
零成本抽象不是(有运行时开销,哪怕没异常发生)是(普通返回值,零运行时机制)

「零成本」是 Zig 选择的硬核理由:没有异常发生时,异常机制本身不花一分钱 (补充,不在书里,来自 Zero-Cost Exceptions 讨论的通行结论;C++ 表格式异常处理也接近零成本, 但语言不强制你处理——看得见和零成本在 Zig 里是同时成立的)。

9.5.2 错误并集在内存里是什么

ParseError!f64 不是魔法:它是一个带标签的联合——正常时装 f64, 失败时装错误码。这和 §4 的 tagged union 是同一个东西,编译器帮你拼好了而已。 知道这个等价关系,两件事立刻想通:为什么能 switch 匹配错误、 为什么 error return trace 能在每一层留下记录(每层 return 都是一次普通的函数返回, 返回的是一个普通值)。

9.5.3 错误与 optional 的配合(原书没写、代码里天天见)

?T 表达「可能没有值」,!T 表达「可能失败」,两者会同时出现: 函数「找东西」常常写成 !?Item(失败或没找到)或 ?Item(不失败但可能没找到)。 配合的固定套路:

  const item = (try findItem(key)) orelse return error.NotFound;  // 先 try 错误,再 orelse 处理空
  const opt = findItem(key) catch |err| switch (err) { ... };      // 先 catch 错误,再拿 optional

先剥哪一层由你定,规则只有一条:每一层剥的时候都在场——不存在「两种失败混在一起没人管」的状态。 (补充,不在书里,来自 Zig 代码惯例。)

10. 可带走的

主线例子,一行走完: bottomFunction 里 return error.FileNotFound → try 逐层上交 → main 退出 → trace 打印四站行程单; allocateResource:申请 → errdefer free 登记 → 后续失败 → errdefer 触发 → 不泄漏。

  1. Zig 没有异常:可能失败的函数把这件事写进返回类型(!T),失败路径在签名上可见;
  2. error set 是「这个函数可能出哪些错」的显式清单,像枚举一样可 switch、可穷举检查;
  3. !T 读法:返回值,或一个错误;! 在签名里专表错误,不是逻辑非;
  4. try = 成功取值、失败替我 return;catch = 就地接错误,或给默认值;
  5. 小错误集自动升级成大错误集——底层具体、上层笼统,层次设计成立;
  6. errdefer 只在「带着错误离开」时执行——「申请-登记释放」两行相邻写,泄漏结构性地不可能;
  7. 裸联合共享内存、不知道激活字段,访问错了运行期 panic;带标签联合由类型记住, 配 switch 穷举检查,是「多形态结果」的安全表达;
  8. 错误返回追踪记录错误经过的每一站(Debug/ReleaseSafe 默认开)——看到的不只是爆炸点, 还有炸弹的来路;
  9. catch unreachable 的判据和 unreachable 相同:被别处保证,不是我觉得;
  10. 处置三姿势之外没有第四种:每个失败调用点,要么 try、要么 catch、要么编译报错—— 「假装没看见」不是选项。

11. 原文地图

主题原书章原文位置
错误处理哲学(问责伙伴)Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:26(搜「first-class citizens」) · text/12-ch06-chapter-6-error-handling.txt:628(搜「accountability partner」)
枚举与 @intFromEnumChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:79(搜「starting from zero」) · text/12-ch06-chapter-6-error-handling.txt:90(搜「day number 2」)
自定义枚举值Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:95(搜「OK = 200」)
枚举方法Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:119(搜「isSafeToGo」)
枚举 + switchChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:143(搜「switch (day)」) · text/12-ch06-chapter-6-error-handling.txt:152(搜「TGIF」)
枚举字面量Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:160(搜「const day: Day = .Saturday」) · text/12-ch06-chapter-6-error-handling.txt:170(搜「just an enum literal」)
非穷举枚举Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:209(搜「This underscore is crucial」) · text/12-ch06-chapter-6-error-handling.txt:217(搜「catch-all case」)
@tagName 与 @typeInfoChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:244(搜「@tagName(day)」) · text/12-ch06-chapter-6-error-handling.txt:254(搜「@typeInfo(Day)」)
裸联合定义Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:279(搜「const Data = union」) · text/12-ch06-chapter-6-error-handling.txt:285(搜「only one of them」)
访问 inactive 字段 panicChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:319(搜「access of union field」)
整体重赋换激活字段Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:325(搜「Data{ .floatValue = 2.718 }」)
带标签联合Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:338(搜「union(enum)」) · text/12-ch06-chapter-6-error-handling.txt:351(搜「Result{ .Success = input * 2 }」)
switch 修改载荷Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:380(搜「
union 方法与匿名字面量Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:407(搜「@tagName(self) ==」) · text/12-ch06-chapter-6-error-handling.txt:437(搜「.{ .intValue = 256 }」)
错误集Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:465(搜「const NetworkError = error{」)
fetchData 与 ! 语义Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:477(搜「fn fetchData」) · text/12-ch06-chapter-6-error-handling.txt:486(搜「may return an error from its error set」)
! 符号专讲Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:507(搜「indicate that a function may return an error」)
try 的语义Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:491(搜「try keyword attempts」)
catch 就地处理Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:511(搜「catch
错误并集 parseFloatChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:533(搜「ParseError!f64」)
错误集升级Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:584(搜「coerced into GeneralError」)
catch 默认值与 catch unreachableChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:593(搜「catch 0.0」) · text/12-ch06-chapter-6-error-handling.txt:600(搜「catch unreachable」)
errdeferChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:606(搜「only if an error is returned」) · text/12-ch06-chapter-6-error-handling.txt:613(搜「errdefer allocator.free」)
错误返回追踪Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:663(搜「play-by-play」) · text/12-ch06-chapter-6-error-handling.txt:685(搜「return error.FileNotFound」)
trace 示例输出Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:705(搜「Error: FileNotFound」)
trace 默认档位Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:715(搜「Debug and ReleaseSafe builds」)
anyerrorChapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:725(搜「global error set anyerror」)
最佳实践四条Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:741(搜「Use explicit error types」)
章末 31 题Chapter 6: Error Handlingtext/12-ch06-chapter-6-error-handling.txt:787(搜「choose the correct answer」) · text/12-ch06-chapter-6-error-handling.txt:997(搜「Answers」)

Footnotes

  1. 出处:「Chapter 6: Error Handling」第 47–66 段(text/12-ch06-chapter-6-error-handling.txt:49,搜「magic numbers」)。原文:散布魔数是「确保你未来的自己恨你」的好办法。 ↩

  2. 出处:同章第 79–90 段(text/12-ch06-chapter-6-error-handling.txt:79,搜「starting from zero」;text/12-ch06-chapter-6-error-handling.txt:90,搜「day number 2」)。 ↩

  3. 出处:同章第 92–109 段(text/12-ch06-chapter-6-error-handling.txt:95,搜「OK = 200」)。 ↩

  4. 出处:同章第 110–132 段(text/12-ch06-chapter-6-error-handling.txt:119,搜「isSafeToGo」)。 ↩

  5. 出处:同章第 134–152 段(text/12-ch06-chapter-6-error-handling.txt:143,搜「switch (day)」;text/12-ch06-chapter-6-error-handling.txt:152,搜「TGIF」)。 ↩

  6. 出处:同章第 154–166 段(text/12-ch06-chapter-6-error-handling.txt:160,搜「const day: Day = .Saturday」)。 ↩

  7. 出处:同章第 168–170 段(text/12-ch06-chapter-6-error-handling.txt:170,搜「just an enum literal」)。 ↩

  8. 出处:同章第 172–201 段(text/12-ch06-chapter-6-error-handling.txt:200,搜「fail to compile」)。 ↩

  9. 出处:同章第 203–231 段(text/12-ch06-chapter-6-error-handling.txt:209,搜「This underscore is crucial」;text/12-ch06-chapter-6-error-handling.txt:217,搜「catch-all case」)。 ↩

  10. 出处:同章第 239–248 段(text/12-ch06-chapter-6-error-handling.txt:244,搜「@tagName(day)」)。 ↩

  11. 出处:同章第 250–265 段(text/12-ch06-chapter-6-error-handling.txt:254,搜「@typeInfo(Day)」)。 ↩

  12. 出处:同章第 273–286 段(text/12-ch06-chapter-6-error-handling.txt:279,搜「const Data = union」;text/12-ch06-chapter-6-error-handling.txt:285,搜「only one of them」)。 ↩

  13. 出处:同章第 309–321 段(text/12-ch06-chapter-6-error-handling.txt:319,搜「access of union field」;text/12-ch06-chapter-6-error-handling.txt:320,搜「while field 'intValue' is active」)。 ↩ ↩2

  14. 出处:同章第 323–328 段(text/12-ch06-chapter-6-error-handling.txt:325,搜「Data{ .floatValue = 2.718 }」)。 ↩

  15. 出处:同章第 330–344 段(text/12-ch06-chapter-6-error-handling.txt:334,搜「combines a union with an enum」)。 ↩

  16. 出处:同章第 345–371 段(text/12-ch06-chapter-6-error-handling.txt:351,搜「Result{ .Success = input * 2 }」)。 ↩

  17. 出处:同章第 373–394 段(text/12-ch06-chapter-6-error-handling.txt:380,搜「|*value|」)。 ↩

  18. 出处:同章第 396–421 段(text/12-ch06-chapter-6-error-handling.txt:407,搜「@tagName(self) ==」)。 ↩

  19. 出处:同章第 423–447 段(text/12-ch06-chapter-6-error-handling.txt:437,搜「.{ .intValue = 256 }」)。 ↩

  20. 出处:同章第 459–488 段(text/12-ch06-chapter-6-error-handling.txt:465,搜「const NetworkError = error{」;text/12-ch06-chapter-6-error-handling.txt:460,搜「similar to an enum」)。 ↩

  21. 出处:同章第 633–646 段(text/12-ch06-chapter-6-error-handling.txt:643,搜「precise, structured, and professional」)。 ↩

  22. 出处:同章第 474–488 段(text/12-ch06-chapter-6-error-handling.txt:477,搜「fn fetchData」;text/12-ch06-chapter-6-error-handling.txt:486,搜「may return an error from its error set」)。 ↩

  23. 出处:同章第 502–507 段(text/12-ch06-chapter-6-error-handling.txt:507,搜「indicate that a function may return an error」)。 ↩ ↩2

  24. 出处:同章第 490–500 段(text/12-ch06-chapter-6-error-handling.txt:491,搜「try keyword attempts」)。 ↩ ↩2

  25. 出处:同章第 509–518 段(text/12-ch06-chapter-6-error-handling.txt:511,搜「catch |err|」)与第 590–596 段(text/12-ch06-chapter-6-error-handling.txt:593,搜「catch 0.0」)。 ↩

  26. 出处:同章第 562–588 段(text/12-ch06-chapter-6-error-handling.txt:584,搜「coerced into GeneralError」)。 ↩ ↩2

  27. 出处:同章第 598–603 段(text/12-ch06-chapter-6-error-handling.txt:600,搜「catch unreachable」)。 ↩

  28. 出处:同章第 605–625 段(text/12-ch06-chapter-6-error-handling.txt:606,搜「only if an error is returned」;text/12-ch06-chapter-6-error-handling.txt:613,搜「errdefer allocator.free」)。 ↩ ↩2

  29. 出处:同章第 648–671 段(text/12-ch06-chapter-6-error-handling.txt:663,搜「play-by-play」)。 ↩

  30. 出处:同章第 703–712 段(text/12-ch06-chapter-6-error-handling.txt:705,搜「Error: FileNotFound」)。 ↩

  31. 出处:同章第 714–718 段(text/12-ch06-chapter-6-error-handling.txt:715,搜「Debug and ReleaseSafe builds」)。 ↩ ↩2

  32. 出处:同章第 723–726 段(text/12-ch06-chapter-6-error-handling.txt:725,搜「global error set anyerror」)。 ↩

  33. 出处:同章第 627–631 段(text/12-ch06-chapter-6-error-handling.txt:628,搜「accountability partner」)。 ↩

  34. 出处:同章第 633–646 段(text/12-ch06-chapter-6-error-handling.txt:639,搜「expected but also defined and documented」)。 ↩

  35. 出处:同章第 739–748 段(text/12-ch06-chapter-6-error-handling.txt:741,搜「Use explicit error types」)。 ↩

  36. 出处:同章第 785–1028 段(text/12-ch06-chapter-6-error-handling.txt:787,搜「choose the correct answer」;text/12-ch06-chapter-6-error-handling.txt:997,搜「Answers」)。 ↩

On this page