From 55678ebdf3a4f15d440c20e8b1a4b56b22e5d3f3 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Thu, 5 Feb 2026 21:38:23 +0800 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=BC=96=E5=86=99=E8=A7=84=E8=8C=83=E6=8C=87=E5=8D=97?= =?UTF-8?q?=E4=BB=A5=E7=A1=AE=E4=BF=9D=E4=BB=A3=E7=A0=81=E5=8F=AF=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index a61daa88..84327ace 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -251,3 +251,254 @@ zig build cd course/code/15/build_system zig build ``` + +## 文档编写规范 + +### 文档与代码分离原则 + +本项目要求**代码示例必须与 Markdown 文档分离**,不允许将代码内联到 Markdown 中。这样做的目的是: + +1. **可测试性**:所有代码示例都能通过 `zig build` 进行编译验证 +2. **版本兼容性**:不同 Zig 版本可以维护各自的代码实现 +3. **代码质量**:通过单元测试确保示例代码的正确性 + +### 添加新文档章节的完整流程 + +以添加一个新的教程章节为例,完整流程如下: + +#### 步骤 1:创建代码示例文件 + +**路径**: `course/code/15/.zig`(15 为当前活跃版本) + +代码文件结构规范: + +```zig +const std = @import("std"); + +// 主入口函数,用于运行所有示例 +pub fn main() !void { + Example1.main(); + Example2.main(); + try Example3.main(); +} + +// 使用结构体封装每个代码片段 +const Example1 = struct { + // #region example1_anchor + // 这里是将在文档中显示的代码 + const SomeType = struct { + field: u32, + }; + + pub fn main() void { + const val: SomeType = .{ .field = 42 }; + std.debug.print("value: {}\n", .{val.field}); + } + // #endregion example1_anchor +}; + +const Example2 = struct { + // #region example2_anchor + // 另一个代码片段 + pub fn main() void { + // ... + } + // #endregion example2_anchor +}; + +// 文件末尾添加单元测试 +test "example1 test" { + // 测试 Example1 的功能 +} + +test "example2 test" { + // 测试 Example2 的功能 +} +``` + +**关键规范**: + +- 使用 `#region ` 和 `#endregion ` 标记代码片段 +- 每个逻辑片段封装在独立的结构体中 +- 提供 `pub fn main()` 用于运行时验证 +- 提供 `test` 用于单元测试验证 +- anchor 名称使用 `snake_case` 风格 + +#### 步骤 2:创建 Markdown 文档 + +**路径**: `course//.md` + +文档结构规范: + +```markdown +--- +outline: deep +--- + +# 章节标题 + +> 简短的章节描述或引言 + +正文内容... + +## 小节标题 + +解释性文字... + +<<<@/code/release/.zig#example1_anchor + +更多解释... + +<<<@/code/release/.zig#example2_anchor +``` + +**代码引用语法**: + +- `<<<@/code/release/.zig#` - 引用指定锚点的代码片段 +- `release` 是符号链接,指向当前最新稳定版本(如 15) +- 锚点名称必须与代码文件中的 `#region` 名称完全匹配 + +#### 步骤 3:更新导航配置 + +**路径**: `course/.vitepress/sidebar.ts` + +在适当的章节中添加新页面: + +```typescript +{ + text: "进阶学习", + items: [ + // ... 其他条目 + { + text: "新章节标题", + link: "/advanced/new-topic", + }, + ], +}, +``` + +#### 步骤 4:验证和格式化 + +```bash +# 1. 验证代码编译 +zig build + +# 2. 运行代码测试 +zig build test + +# 3. 格式化所有文件 +bun format + +# 4. 检查格式 +bun check + +# 5. 本地预览文档 +bun dev +``` + +### 代码片段标记详解 + +#### 基本标记 + +```zig +// #region anchor_name +const Example = struct { + // 代码内容 +}; +// #endregion anchor_name +``` + +#### 嵌套标记(用于显示不同详细程度) + +```zig +// #region more_example +const std = @import("std"); + +// #region default_example +const Point = struct { + x: i32, + y: i32, +}; +// #endregion default_example + +pub fn main() void { + const pt: Point = .{ .x = 1, .y = 2 }; + std.debug.print("({}, {})\n", .{ pt.x, pt.y }); +} +// #endregion more_example +``` + +在 Markdown 中可以选择性引用: + +```markdown +::: code-group + +<<<@/code/release/example.zig#default_example [default] + +<<<@/code/release/example.zig#more_example [more] + +::: +``` + +### 文档样式规范 + +#### Markdown 格式 + +- 使用 `---` 分隔的 front matter,通常包含 `outline: deep` +- 一级标题 `#` 用于页面主标题 +- 二级标题 `##` 用于主要章节 +- 三级标题 `###` 用于子章节 + +#### 提示框语法 + +```markdown +:::info 🅿️ 提示 +提示内容 +::: + +:::warning 警告 +警告内容 +::: + +:::details 可折叠内容标题 +详细内容 +::: +``` + +#### 中英文排版 + +- 中英文之间添加空格:`这是 Zig 语言` +- 技术术语首次出现时标注英文:`**结果位置语义(Result Location Semantics)**` +- 代码相关使用反引号:`` `comptime` `` + +### 多版本代码维护 + +当需要为不同 Zig 版本维护不同代码时: + +1. 在对应版本目录创建代码文件:`course/code/14/.zig`、`course/code/15/.zig` +2. 如果版本间完全兼容,使用符号链接避免重复 +3. 在 Markdown 中始终使用 `release` 引用,自动指向最新版本 + +### 完整示例:添加"结果位置语义"章节 + +``` +1. 创建代码文件 + course/code/15/result-location.zig + - 包含所有示例的结构体封装 + - 使用 #region/#endregion 标记 + - 添加 main() 函数和单元测试 + +2. 创建文档文件 + course/advanced/result-location.md + - front matter 配置 + - 使用 <<<@/code/release/result-location.zig#anchor 引用代码 + +3. 更新导航 + course/.vitepress/sidebar.ts + - 在"进阶学习"部分添加条目 + +4. 验证 + zig build # 编译验证 + bun format # 格式化 + bun dev # 本地预览 +``` From 13be2c9e35ecd533a74b6f7eb5c155a35c7e0aa1 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Thu, 5 Feb 2026 21:38:31 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E4=BD=8D=E7=BD=AE=E8=AF=AD=E4=B9=89=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=92=8C=E7=A4=BA=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- course/.vitepress/sidebar.ts | 4 + course/advanced/result-location.md | 119 ++++++++ course/basic/advanced_type/struct.md | 2 +- course/code/15/result-location.zig | 390 +++++++++++++++++++++++++++ 4 files changed, 514 insertions(+), 1 deletion(-) create mode 100644 course/advanced/result-location.md create mode 100644 course/code/15/result-location.zig diff --git a/course/.vitepress/sidebar.ts b/course/.vitepress/sidebar.ts index ec234449..f6aa15b6 100644 --- a/course/.vitepress/sidebar.ts +++ b/course/.vitepress/sidebar.ts @@ -147,6 +147,10 @@ export default [ text: "类型转换", link: "/advanced/type_cast", }, + { + text: "结果位置语义", + link: "/advanced/result-location", + }, { text: "内存管理", link: "/advanced/memory_manage", diff --git a/course/advanced/result-location.md b/course/advanced/result-location.md new file mode 100644 index 00000000..d3887782 --- /dev/null +++ b/course/advanced/result-location.md @@ -0,0 +1,119 @@ +--- +outline: deep +--- + +# 结果位置语义 + +> Result Location Semantics 是 Zig 类型系统的核心机制之一,它允许编译器根据上下文自动推断类型。 + +在 Zig 中,许多表达式的类型不是由表达式本身决定的,而是由表达式的**使用方式**决定的。这种机制被称为**结果位置语义(Result Location Semantics)**。 + +## 什么是结果位置语义 + +结果位置语义是指编译器根据表达式结果的**存储位置**(即"结果位置")来推断表达式类型的机制。 + +简单来说,当你写 `.{ .x = 1, .y = 2 }` 这样的表达式时,编译器会查看这个值将被赋给什么类型的变量,然后自动推断出正确的类型。 + +<<<@/code/release/result-location.zig#basic_inference + +## 结果类型 + +**结果类型(Result Type)** 是编译器期望表达式产生的类型。它由表达式的上下文决定: + +### 变量声明 + +当变量有显式类型声明时,右侧表达式的结果类型就是该变量的类型: + +<<<@/code/release/result-location.zig#result_type_variable + +### 函数返回值 + +函数的返回类型决定了 `return` 表达式的结果类型: + +<<<@/code/release/result-location.zig#result_type_return + +### 函数参数 + +函数参数类型决定了调用时传入表达式的结果类型: + +<<<@/code/release/result-location.zig#result_type_param + +### 结构体字段默认值 + +结构体字段的类型决定了默认值表达式的结果类型: + +<<<@/code/release/result-location.zig#result_type_field_default + +## 结果位置 + +**结果位置(Result Location)** 是指表达式结果将被存储的内存位置。Zig 编译器会将结果位置的信息传递给子表达式,这使得某些优化成为可能。 + +### 嵌套结构的结果位置传播 + +结果位置信息会传播到嵌套的子表达式: + +<<<@/code/release/result-location.zig#result_location_nested + +## 声明字面量 + +**声明字面量(Decl Literals)** 是 Zig 0.14.0 引入的新语法特性。它扩展了枚举字面量语法 `.foo`,使其不仅可以引用枚举成员,还可以引用目标类型上的任何声明。 + +### 基本用法 + +<<<@/code/release/result-location.zig#decl_literal_basic + +由于 `val` 的类型是 `S`,编译器知道 `.default` 应该在 `S` 的命名空间中查找,因此 `.default` 等价于 `S.default`。 + +### 在结构体字段默认值中使用 + +声明字面量在设置字段默认值时特别有用: + +<<<@/code/release/result-location.zig#decl_literal_field_default + +### 调用函数 + +声明字面量也支持调用函数: + +<<<@/code/release/result-location.zig#decl_literal_function + +### 与错误联合类型配合 + +声明字面量支持通过 `try` 调用返回错误联合的函数: + +<<<@/code/release/result-location.zig#decl_literal_error_union + +## 避免错误的字段默认值 + +声明字面量的一个重要应用是避免**错误的字段默认值(Faulty Default Field Values)**问题。 + +### 问题示例 + +考虑以下代码: + +<<<@/code/release/result-location.zig#faulty_default_problem + +这里的问题是 `ptr` 和 `len` 是相互关联的,但默认值允许用户只覆盖其中一个,导致数据不一致。 + +### 使用声明字面量的解决方案 + +<<<@/code/release/result-location.zig#faulty_default_solution + +## 标准库中的应用 + +从 Zig 0.14.0 开始,标准库中的许多类型都采用了声明字面量模式。 + +### ArrayListUnmanaged + +<<<@/code/release/result-location.zig#stdlib_arraylist + +### GeneralPurposeAllocator + +<<<@/code/release/result-location.zig#stdlib_gpa + +## 字段和声明不可重名 + +Zig 0.14.0 引入了一项限制:容器类型(`struct`、`union`、`enum`、`opaque`)的字段和声明不能同名。 + +<<<@/code/release/result-location.zig#naming_conflict + +这个限制是为了消除 `MyType.foo` 到底是访问字段还是声明的歧义。 diff --git a/course/basic/advanced_type/struct.md b/course/basic/advanced_type/struct.md index 19874f9c..bac7b519 100644 --- a/course/basic/advanced_type/struct.md +++ b/course/basic/advanced_type/struct.md @@ -103,7 +103,7 @@ 在前面我们学习了结构体字面量的完整语法 `StructName{ .field = value }`。当 Zig 编译器能够从上下文推断出结构体类型时,可以省略类型名称,使用简写语法 `.{ .field = value }`。 -这种机制被称为**结果位置语义(Result Location Semantics)**。 +这种机制被称为**[结果位置语义(Result Location Semantics)](../../advanced/result-location.md)**。 <<<@/code/release/struct.zig#struct_init_inferred diff --git a/course/code/15/result-location.zig b/course/code/15/result-location.zig new file mode 100644 index 00000000..8733d0d4 --- /dev/null +++ b/course/code/15/result-location.zig @@ -0,0 +1,390 @@ +const std = @import("std"); + +pub fn main() !void { + BasicInference.main(); + ResultTypeVariable.main(); + ResultTypeReturn.main(); + ResultTypeParam.main(); + ResultTypeFieldDefault.main(); + ResultLocationNested.main(); + DeclLiteralBasic.main(); + DeclLiteralFieldDefault.main(); + DeclLiteralFunction.main(); + // 以下示例需要 allocator,仅在测试中运行 + // try DeclLiteralErrorUnion.main(); + // try StdLibArrayList.main(); + try StdLibGPA.main(); +} + +const BasicInference = struct { + // #region basic_inference + const Point = struct { + x: i32, + y: i32, + }; + + pub fn main() void { + // 编译器从变量类型推断出 .{} 的具体类型 + const pt: Point = .{ .x = 10, .y = 20 }; + + // 等价于 + const pt2: Point = Point{ .x = 10, .y = 20 }; + + std.debug.print("pt: ({}, {}), pt2: ({}, {})\n", .{ pt.x, pt.y, pt2.x, pt2.y }); + } + // #endregion basic_inference +}; + +const ResultTypeVariable = struct { + // #region result_type_variable + const Color = struct { + r: u8, + g: u8, + b: u8, + }; + + pub fn main() void { + // 结果类型是 Color + const red: Color = .{ .r = 255, .g = 0, .b = 0 }; + std.debug.print("red: ({}, {}, {})\n", .{ red.r, red.g, red.b }); + } + // #endregion result_type_variable +}; + +const ResultTypeReturn = struct { + // #region result_type_return + const Vec2 = struct { + x: f32, + y: f32, + }; + + fn origin() Vec2 { + // 结果类型是 Vec2 + return .{ .x = 0, .y = 0 }; + } + + pub fn main() void { + const o = origin(); + std.debug.print("origin: ({d}, {d})\n", .{ o.x, o.y }); + } + // #endregion result_type_return +}; + +const ResultTypeParam = struct { + // #region result_type_param + const Size = struct { + width: u32, + height: u32, + }; + + fn calculateArea(size: Size) u64 { + return @as(u64, size.width) * size.height; + } + + pub fn main() void { + // 调用时,.{} 的结果类型是 Size + const area = calculateArea(.{ .width = 100, .height = 50 }); + std.debug.print("area: {}\n", .{area}); + } + // #endregion result_type_param +}; + +const ResultTypeFieldDefault = struct { + // #region result_type_field_default + const Config = struct { + timeout: u32 = 30, + retries: u8 = 3, + }; + + const Wrapper = struct { + // 字段类型是 Config,所以 .{} 的结果类型是 Config + config: Config = .{}, + }; + + pub fn main() void { + const w: Wrapper = .{}; + std.debug.print("timeout: {}, retries: {}\n", .{ w.config.timeout, w.config.retries }); + } + // #endregion result_type_field_default +}; + +const ResultLocationNested = struct { + // #region result_location_nested + const Inner = struct { + value: i32, + }; + + const Outer = struct { + inner: Inner, + name: []const u8, + }; + + pub fn main() void { + // 结果位置 Outer 传播到 inner 字段,使其结果类型为 Inner + const obj: Outer = .{ + .inner = .{ .value = 42 }, // 这里 .{} 的结果类型是 Inner + .name = "example", + }; + std.debug.print("inner.value: {}, name: {s}\n", .{ obj.inner.value, obj.name }); + } + // #endregion result_location_nested +}; + +const DeclLiteralBasic = struct { + // #region decl_literal_basic + const S = struct { + x: u32, + + // 类型内的常量声明 + const default: S = .{ .x = 123 }; + }; + + pub fn main() void { + // .default 会被解析为 S.default + const val: S = .default; + std.debug.print("val.x: {}\n", .{val.x}); + } + + test "decl literal" { + const val: S = .default; + try std.testing.expectEqual(123, val.x); + } + // #endregion decl_literal_basic +}; + +const DeclLiteralFieldDefault = struct { + // #region decl_literal_field_default + const Settings = struct { + x: u32, + y: u32, + + const default: Settings = .{ .x = 1, .y = 2 }; + const high_performance: Settings = .{ .x = 100, .y = 200 }; + }; + + const Application = struct { + // 使用声明字面量设置默认值 + settings: Settings = .default, + }; + + pub fn main() void { + const app1: Application = .{}; + std.debug.print("app1.settings: ({}, {})\n", .{ app1.settings.x, app1.settings.y }); + + // 也可以覆盖为其他预定义值 + const app2: Application = .{ .settings = .high_performance }; + std.debug.print("app2.settings: ({}, {})\n", .{ app2.settings.x, app2.settings.y }); + } + + test "decl literal in field default" { + const app1: Application = .{}; + try std.testing.expectEqual(1, app1.settings.x); + + const app2: Application = .{ .settings = .high_performance }; + try std.testing.expectEqual(100, app2.settings.x); + } + // #endregion decl_literal_field_default +}; + +const DeclLiteralFunction = struct { + // #region decl_literal_function + const Point = struct { + x: i32, + y: i32, + + fn init(val: i32) Point { + return .{ .x = val, .y = val }; + } + + fn offset(val: i32, dx: i32, dy: i32) Point { + return .{ .x = val + dx, .y = val + dy }; + } + }; + + pub fn main() void { + // .init(5) 等价于 Point.init(5) + const p1: Point = .init(5); + std.debug.print("p1: ({}, {})\n", .{ p1.x, p1.y }); + + const p2: Point = .offset(0, 10, 20); + std.debug.print("p2: ({}, {})\n", .{ p2.x, p2.y }); + } + + test "call function via decl literal" { + const p1: Point = .init(5); + try std.testing.expectEqual(5, p1.x); + try std.testing.expectEqual(5, p1.y); + + const p2: Point = .offset(0, 10, 20); + try std.testing.expectEqual(10, p2.x); + try std.testing.expectEqual(20, p2.y); + } + // #endregion decl_literal_function +}; + +const DeclLiteralErrorUnion = struct { + // #region decl_literal_error_union + const Buffer = struct { + data: std.ArrayListUnmanaged(u32), + + fn initCapacity(allocator: std.mem.Allocator, capacity: usize) !Buffer { + return .{ .data = try .initCapacity(allocator, capacity) }; + } + }; + + test "decl literal with error union" { + var buf: Buffer = try .initCapacity(std.testing.allocator, 10); + defer buf.data.deinit(std.testing.allocator); + + buf.data.appendAssumeCapacity(42); + try std.testing.expectEqual(42, buf.data.items[0]); + } + // #endregion decl_literal_error_union +}; + +const FaultyDefaultValues = struct { + // #region faulty_default_problem + /// `ptr` 指向 `[len]u32` + pub const BufferA = extern struct { + ptr: ?[*]u32 = null, + len: usize = 0, + }; + + // 看起来是空 buffer + var empty_buf: BufferA = .{}; + + // 但用户可以只覆盖部分字段,导致不一致的状态! + var bad_buf: BufferA = .{ .len = 10 }; // ptr 是 null,但 len 是 10 + // #endregion faulty_default_problem +}; + +const FaultyDefaultSolution = struct { + // #region faulty_default_solution + /// `ptr` 指向 `[len]u32` + pub const BufferB = extern struct { + ptr: ?[*]u32, + len: usize, + + // 通过声明提供预定义的有效状态 + pub const empty: BufferB = .{ .ptr = null, .len = 0 }; + }; + + // 安全地创建空 buffer + var empty_buf: BufferB = .empty; + + // 如果要手动指定值,必须同时指定所有字段 + // var custom_buf: BufferB = .{ .ptr = some_ptr, .len = 10 }; + // #endregion faulty_default_solution +}; + +const StdLibArrayList = struct { + // #region stdlib_arraylist + const Container = struct { + // 使用 .empty 而不是 .{} + list: std.ArrayListUnmanaged(i32) = .empty, + }; + + test "ArrayListUnmanaged with decl literal" { + var c: Container = .{}; + defer c.list.deinit(std.testing.allocator); + + try c.list.append(std.testing.allocator, 1); + try c.list.append(std.testing.allocator, 2); + + try std.testing.expectEqual(2, c.list.items.len); + } + // #endregion stdlib_arraylist +}; + +const StdLibGPA = struct { + // #region stdlib_gpa + pub fn main() !void { + // 使用 .init 进行初始化 + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + const ptr = try allocator.alloc(u8, 100); + defer allocator.free(ptr); + + std.debug.print("allocated {} bytes\n", .{ptr.len}); + } + + test "GPA with decl literal" { + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + const ptr = try allocator.alloc(u8, 100); + defer allocator.free(ptr); + + try std.testing.expectEqual(100, ptr.len); + } + // #endregion stdlib_gpa +}; + +const NamingConflict = struct { + // #region naming_conflict + // 错误:字段和声明同名(此代码无法编译) + // const Bad = struct { + // Value: u32, // 字段 + // const Value = 100; // 声明 - 编译错误! + // }; + + // 正确:遵循命名约定 + const Good = struct { + value: u32, // 字段使用 snake_case + const Value = 100; // 声明使用 PascalCase + }; + // #endregion naming_conflict +}; + +test "basic inference" { + const Point = BasicInference.Point; + const pt: Point = .{ .x = 10, .y = 20 }; + try std.testing.expectEqual(10, pt.x); + try std.testing.expectEqual(20, pt.y); +} + +test "decl literal basic" { + const S = DeclLiteralBasic.S; + const val: S = .default; + try std.testing.expectEqual(123, val.x); +} + +test "decl literal field default" { + const Application = DeclLiteralFieldDefault.Application; + const app1: Application = .{}; + try std.testing.expectEqual(1, app1.settings.x); +} + +test "decl literal function" { + const Point = DeclLiteralFunction.Point; + const p1: Point = .init(5); + try std.testing.expectEqual(5, p1.x); +} + +test "decl literal error union" { + const Buffer = DeclLiteralErrorUnion.Buffer; + var buf: Buffer = try .initCapacity(std.testing.allocator, 10); + defer buf.data.deinit(std.testing.allocator); + buf.data.appendAssumeCapacity(42); + try std.testing.expectEqual(42, buf.data.items[0]); +} + +test "stdlib arraylist" { + const Container = StdLibArrayList.Container; + var c: Container = .{}; + defer c.list.deinit(std.testing.allocator); + try c.list.append(std.testing.allocator, 1); + try std.testing.expectEqual(1, c.list.items.len); +} + +test "stdlib gpa" { + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + const ptr = try allocator.alloc(u8, 100); + defer allocator.free(ptr); + try std.testing.expectEqual(100, ptr.len); +} From c33ae90973f2f09b8d206675dc8efc6fdd16f0d8 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Thu, 5 Feb 2026 23:32:23 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs(course):=20=E7=A7=BB=E9=99=A4"?= =?UTF-8?q?=E6=96=B0=E8=AF=AD=E6=B3=95=E7=89=B9=E6=80=A7"=E7=9A=84?= =?UTF-8?q?=E5=86=97=E4=BD=99=E8=A1=A8=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将"新语法特性"改为"语法特性",避免与前文"引入"重复,使表达更简洁。 --- course/advanced/result-location.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/course/advanced/result-location.md b/course/advanced/result-location.md index d3887782..80d28518 100644 --- a/course/advanced/result-location.md +++ b/course/advanced/result-location.md @@ -56,7 +56,7 @@ outline: deep ## 声明字面量 -**声明字面量(Decl Literals)** 是 Zig 0.14.0 引入的新语法特性。它扩展了枚举字面量语法 `.foo`,使其不仅可以引用枚举成员,还可以引用目标类型上的任何声明。 +**声明字面量(Decl Literals)** 是 Zig 0.14.0 引入的语法特性。它扩展了枚举字面量语法 `.foo`,使其不仅可以引用枚举成员,还可以引用目标类型上的任何声明。 ### 基本用法