ECC 规则体系中的 Swift 编码风格:格式、不可变性、Swift 6 类型化错误与严格并发的完整实践
在 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 在编辑任意 *.swift 或 Package.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 优先与结构体值语义
规则给出的两条具体约定:
- 优先
let,而非var—— 先按let定义,只有编译器要求时才改成var; - 默认使用
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),并给出三条优先级建议:
Sendable值类型用于跨越隔离边界(isolation boundary)传递的数据;- actor 用于承载共享可变状态;
- 结构化并发(
async let、TaskGroup)优先于非结构化的裸Task {}。
三条建议各有针对的典型问题:裸 Task {} 脱离了调用方的生命周期与取消传播,容易泄漏任务和“忘记 await”的未处理失败;而 async let 与 TaskGroup 保证子任务随作用域完成、可统一取消、错误可聚合上抛。共享可变状态用 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 中的作用可以概括为三点:
- 分层继承:通用规则定“不可变、显式错误、边界校验”等硬约束,本文件负责把它们翻译为 Swift 的具体机制——
let/struct、throws(LoadError)、Sendable/actor/结构化并发; - 按需触发:通过
globs精确限定为*.swift与Package.swift,alwaysApply: false控制上下文成本,与 rules/swift/ 下的paths:变体共同支撑多 harness 分发; - 工具链闭环:SwiftFormat/SwiftLint/
swift build由 hooks 规则自动挂接到编辑后流程,使风格从“约定”变为“每次编辑后自动验证的机制”。
对使用 ECC 的 Swift 项目而言,这意味着 Agent 产出的 Swift 代码会稳定落在同一套风格内:let 优先、结构体值语义、类型化错误通道、严格并发下的 actor 与结构化并发,并且每次编辑后自动经过格式化与类型检查。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00