首页
/ ECC 规则体系中的 Swift 编码风格:格式、不可变性、Swift 6 类型化错误与严格并发的完整实践

ECC 规则体系中的 Swift 编码风格:格式、不可变性、Swift 6 类型化错误与严格并发的完整实践

2026-09-06 22:25:08作者:咎竹峻Karen

在 ECC(agent harness 性能优化系统)的多 harness 规则体系中,.cursor/rules/swift-coding-style.md 是面向 Cursor harness 的 Swift 语言专项规则文件,负责在通用编码风格规则(common coding style)之上叠加 Swift 特有的格式化工具链、let 优先的不可变值语义、遵循 Apple API 设计指南的命名约定、Swift 6 类型化 throws 错误处理,以及严格并发检查下的 Sendable/actor/结构化并发要求。读懂这条规则链,就能掌握 ECC 如何把一套语言无关的工程约束“分层下发”到具体语言,并让 AI Agent 在编辑任意 *.swiftPackage.swift 文件时自动遵循同一套 Swift 编码标准。

规则文件的定位:通用规则 + 语言专项的继承结构

.cursor/rules/swift-coding-style.md 在文件开头明确声明了自身定位:

This file extends the common coding style rule with Swift specific content.(本文件在通用编码风格规则基础上扩展 Swift 专项内容)

也就是说,它不是孤立的风格清单,而是 ECC 规则体系“两层继承”中的第二层。第一层是始终生效的通用规则,在 Cursor 侧对应 .cursor/rules/common-coding-style.md(在通用规则目录中对应 rules/common/coding-style.md),它定义了跨语言的硬性约束:

  • 不可变性(标记为 CRITICAL):永远创建新对象、永不原地修改现有对象,理由是消除隐藏副作用、简化调试并支持安全并发;
  • 文件组织:多小文件优于少大文件,典型 200–400 行、上限 800 行,按功能/领域而非类型组织;
  • 错误处理:每一层显式处理错误,服务端记录详细上下文,绝不静默吞掉错误;
  • 输入校验:在系统边界处校验所有外部数据,快速失败并给出清晰错误;
  • 完成前质量检查清单:函数小于 50 行、文件小于 800 行、嵌套不超过 4 层、无硬编码值、使用不可变模式等。

Swift 专项规则的价值,就是把这些抽象约束翻译为 Swift 的具体语法选择:不可变用 let/struct 落实,错误处理用 Swift 6 类型化 throws 落实,并发安全用 Sendable 与 actor 落实。

Frontmatter:规则何时被 Cursor 触发

Swift 规则文件顶部使用 YAML frontmatter 声明元数据:

---
description: "Swift coding style extending common rules"
globs: ["**/*.swift", "**/Package.swift"]
alwaysApply: false
---

三个字段各有作用:

  • description 说明该规则“扩展通用规则”的继承关系,便于人和其他 Agent 理解规则谱系;
  • globs: ["**/*.swift", "**/Package.swift"] 是触发条件——只有当 Cursor 会话涉及 .swift 源文件或 Swift Package Manager 清单 Package.swift 时,该规则才会被注入上下文;
  • alwaysApply: false 表明它不常驻上下文,而是按文件类型惰性加载,避免污染非 Swift 项目的上下文预算。

值得对照的一点:同一份内容在 ECC 通用规则目录 rules/swift/coding-style.md 中使用的 frontmatter 格式是 paths: 字段而非 globs:,例如:

---
paths:
  - "**/*.swift"
  - "**/Package.swift"
---

从两份文件的结构对比可以推断,ECC 维护了两份内容一致、frontmatter 方言不同的 Swift 规则:.cursor/rules/ 下的是 Cursor 规则格式(globs + alwaysApply),rules/ 下的是面向其他 harness 的通用格式(paths)。仓库中 scripts/lib/install-targets/cursor-project.js 等安装目标脚本负责在安装/同步时把规则落到对应 harness 的目录结构里,这也解释了为什么两份文件内容要保持一致。

格式化:SwiftFormat + SwiftLint 双工具链

规则给出的格式化策略:

  • SwiftFormat 做自动格式化(auto-formatting);
  • SwiftLint 做风格强制检查(style enforcement);
  • Xcode 16+ 自带的 swift-format 作为备选方案(alternative)。

这里隐含的分工是:SwiftFormat 负责“改代码”(幂等的重排版),SwiftLint 负责“挑毛病”(可配置规则的静态检查),二者互补而非互斥。选择 Xcode 16+ 内置的 swift-format 作为备选的实际意义在于:它不需要额外安装第三方工具,CI 环境和 Xcode 工具链内即可运行,适合约束第三方依赖的项目。

配套的 swift-hooks 规则 进一步说明这套工具链如何挂进 Agent 工作流:在 ~/.claude/settings.json 中配置 PostToolUse 钩子,每次 Agent 编辑 .swift 文件后自动运行 SwiftFormat 重排、SwiftLint 检查,并对修改过的包执行 swift build 做类型检查。也就是说,格式化不是人工习惯,而是被工程化为“编辑后自动执行”的强制环节。该钩子规则同时要求:标记代码中的 print() 语句——生产代码应改用 os.Logger 或结构化日志,这与通用规则“错误与日志要有上下文”的精神一致。

不可变性:let 优先与结构体值语义

规则给出的两条具体约定:

  1. 优先 let,而非 var —— 先按 let 定义,只有编译器要求时才改成 var
  2. 默认使用 struct(值语义) —— 只有在需要身份(identity)或引用语义时才用 class

这与通用规则中“ALWAYS create new objects, NEVER mutate existing ones” 的 CRITICAL 级约束直接对应:在 Swift 中,let + struct 组合让“不可变”成为编译期属性——结构体赋值即整体拷贝,任何字段变更都必须通过 var 副本进行,从语言层面消除了原地修改(mutation)这一最大副作用来源。

可以推断,这一约定还带来两个连带收益:其一,值类型天然可安全地跨执行域传递,为后文的 Sendable 要求铺路;其二,struct 默认拷贝语义使“更新”(返回新值)而非“修改”(原地变更)成为唯一选项,恰好落在通用规则伪代码所要求的 update(original, field, value) → returns new copy 模式上。

命名上,规则要求遵循 Apple API 设计指南(文档中给出的外链为 Swift 官方 API Design Guidelines),并列出三条可操作细则:

  • 使用点清晰(Clarity at the point of use)——删掉不必要的词,避免 getUserById 式的冗余前缀,让调用处读起来自然;
  • 按角色而非类型命名方法属性——比如命名为 find/save 这样的角色动词,而不是暴露实现类型;
  • 常量用 static let 而非全局常量——把常量收进命名空间(类型)内,避免污染全局作用域。

错误处理:Swift 6 类型化 throws 与模式匹配

规则要求启用 typed throws(Swift 6+) 并用模式匹配消费错误,给出的示例:

func load(id: String) throws(LoadError) -> Item {
    guard let data = try? read(from: path) else {
        throw .fileNotFound(id)
    }
    return try decode(data)
}

这段示例浓缩了三个要点:

  • throws(LoadError) 把函数的失败面收窄到单一错误类型 LoadError,调用方无需面对泛型 Error,可以确定性地穷举处理每一种失败;
  • guard let ... else { throw .fileNotFound(id) } 展示了错误作为“带上下文的值”被构造(.fileNotFound(id) 携带了引发失败的资源标识),呼应通用规则中“服务端记录详细错误上下文”的要求;
  • try decode(data) 说明解码失败会沿同一条 throws(LoadError) 通道继续向上传播,整条链路保持错误类型的静态可见性。

对照通用错误处理规则可以看到映射关系:类型化 throws 对应“错误在每一层显式处理”(编译器强制 try/catch,杜绝静默吞错),错误枚举携带关联值对应“给用户友好信息 + 给服务端详细上下文”。另外 swift-testing 规则 中的 #expect(throws: ValidationError.invalidEmail) { ... } 用法表明,类型化错误同样让测试断言可以精确匹配到具体错误 case,而不是仅断言“抛了错”。

并发:严格并发检查下的三件优先事项

规则要求开启 Swift 6 严格并发检查(strict concurrency checking),并给出三条优先级建议:

  1. Sendable 值类型用于跨越隔离边界(isolation boundary)传递的数据;
  2. actor 用于承载共享可变状态;
  3. 结构化并发async letTaskGroup)优先于非结构化的裸 Task {}

三条建议各有针对的典型问题:裸 Task {} 脱离了调用方的生命周期与取消传播,容易泄漏任务和“忘记 await”的未处理失败;而 async letTaskGroup 保证子任务随作用域完成、可统一取消、错误可聚合上抛。共享可变状态用 actor 隔离,则让数据竞争从“运行时 bug”变成“编译期类型错误”。

配套的 swift-patterns 规则(与 rules/swift/patterns.md 同内容)给出了这条并发哲学的完整落地示例:一个受 Sendable 约束的 Repository 协议、用关联值枚举建模的状态(LoadState<T: Sendable>: Sendable)、一个泛型 actor Cache<Key, Value> 用 actor 而非锁或 dispatch queue 保护字典存储,以及用默认参数注入协议依赖的 UserService。这些示例与 coding-style 规则互为表里:coding-style 定“必须怎么做”(严格并发、值类型优先、类型化错误),patterns 展示“长什么样”。规则还指向仓库中的两个 skill 作为延伸阅读:swift-actor-persistence(actor 持久化模式)与 swift-protocol-di-testing(协议依赖注入与测试),分别位于 skills/swift-actor-persistence/skills/swift-protocol-di-testing/ 目录。

规则在 Swift 规则族中的位置

.cursor/rules/ 下 Swift 相关的规则并非只有 coding-style 一份,而是一个按职责切分的规则族,全部共享 globs: ["**/*.swift", "**/Package.swift"] 触发条件:

规则文件 职责
.cursor/rules/swift-coding-style.md 格式化、不可变性、命名、错误处理、并发(本文主体)
.cursor/rules/swift-patterns.md 协议导向设计、值类型建模状态、actor 缓存、依赖注入
.cursor/rules/swift-testing.md 新测试用 Swift Testing(@Test + #expect)、测试隔离、参数化测试、swift test --enable-code-coverage 采集覆盖率
.cursor/rules/swift-hooks.md 编辑后自动 SwiftFormat/SwiftLint/swift build 的 PostToolUse 钩子配置,以及 print() 告警
.cursor/rules/swift-security.md Swift 安全专项约束

其中 swift-testing.md 与本文的两处交叉值得注意:一是它要求测试中用 #expect(throws:) 精确匹配类型化错误,是前文 throws(LoadError) 约定在测试侧的对应面;二是它给出的覆盖率命令 swift test --enable-code-coverage 与 hooks 规则中的 swift build 类型检查一起,构成了 Swift 目标在 ECC 中的完整质量闭环:编辑(hooks 自动格式化)→ 构建(类型 + 并发检查)→ 测试(Swift Testing + 覆盖率)。

小结:一条规则如何约束 Agent 的 Swift 编辑行为

把全文串起来,.cursor/rules/swift-coding-style.md 在 ECC 中的作用可以概括为三点:

  1. 分层继承:通用规则定“不可变、显式错误、边界校验”等硬约束,本文件负责把它们翻译为 Swift 的具体机制——let/structthrows(LoadError)Sendable/actor/结构化并发;
  2. 按需触发:通过 globs 精确限定为 *.swiftPackage.swiftalwaysApply: false 控制上下文成本,与 rules/swift/ 下的 paths: 变体共同支撑多 harness 分发;
  3. 工具链闭环:SwiftFormat/SwiftLint/swift build 由 hooks 规则自动挂接到编辑后流程,使风格从“约定”变为“每次编辑后自动验证的机制”。

对使用 ECC 的 Swift 项目而言,这意味着 Agent 产出的 Swift 代码会稳定落在同一套风格内:let 优先、结构体值语义、类型化错误通道、严格并发下的 actor 与结构化并发,并且每次编辑后自动经过格式化与类型检查。

登录后查看全文
热门项目推荐
相关项目推荐