axiom-ownership-conventions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

borrowing & consuming — Parameter Ownership

borrowing & consuming — 参数所有权

Explicit ownership modifiers for performance optimization and noncopyable type support.
用于性能优化和支持非可复制类型的显式所有权修饰符。

When to Use

适用场景

Use when:
  • Large value types being passed read-only (avoid copies)
  • Working with noncopyable types (
    ~Copyable
    )
  • Reducing ARC retain/release traffic
  • Factory methods that consume builder objects
  • Performance-critical code where copies show in profiling
Don't use when:
  • Simple types (Int, Bool, small structs)
  • Compiler optimization is sufficient (most cases)
  • Readability matters more than micro-optimization
  • You're not certain about the performance impact
适用场景:
  • 以只读方式传递大型值类型(避免拷贝)
  • 处理非可复制类型(
    ~Copyable
  • 减少ARC的保留/释放开销
  • 消耗构建器对象的工厂方法
  • 性能敏感代码,且性能分析显示存在拷贝开销
不适用场景:
  • 简单类型(如Int、Bool、小型结构体)
  • 编译器优化已足够的场景(大多数情况)
  • 可读性比微优化更重要的场景
  • 不确定优化对性能的影响时

Quick Reference

速查参考

ModifierOwnershipCopiesUse Case
(default)Compiler choosesImplicitMost cases
borrowing
Caller keepsExplicit
copy
only
Read-only, large types
consuming
Caller transfersNone neededFinal use, factories
inout
Caller keeps, mutableNoneModify in place
修饰符所有权归属拷贝情况适用场景
(默认)编译器自动选择隐式拷贝大多数场景
borrowing
调用者保留所有权仅显式调用
copy
时才拷贝
只读、大型类型
consuming
调用者转移所有权无需拷贝最终使用、工厂方法
inout
调用者保留所有权,可修改无拷贝原地修改

Default Behavior by Context

不同上下文的默认行为

ContextDefaultReason
Function parameters
borrowing
Most params are read-only
Initializer parameters
consuming
Usually stored in properties
Property setters
consuming
Value is stored
Method
self
borrowing
Methods read self
上下文默认值原因
函数参数
borrowing
大多数参数为只读
初始化器参数
consuming
通常会存储到属性中
属性设置器
consuming
值会被存储
方法的
self
borrowing
方法通常读取self

Patterns

实践模式

Pattern 1: Read-Only Large Struct

模式1:只读大型结构体

swift
struct LargeBuffer {
    var data: [UInt8]  // Could be megabytes
}

// ❌ Default may copy
func process(_ buffer: LargeBuffer) -> Int {
    buffer.data.count
}

// ✅ Explicit borrow — no copy
func process(_ buffer: borrowing LargeBuffer) -> Int {
    buffer.data.count
}
swift
struct LargeBuffer {
    var data: [UInt8]  // 可能占用数兆字节
}

// ❌ 默认行为可能会触发拷贝
func process(_ buffer: LargeBuffer) -> Int {
    buffer.data.count
}

// ✅ 显式borrow — 无拷贝
func process(_ buffer: borrowing LargeBuffer) -> Int {
    buffer.data.count
}

Pattern 2: Consuming Factory

模式2:消耗型工厂方法

swift
struct Builder {
    var config: Configuration

    // Consumes self — builder invalid after call
    consuming func build() -> Product {
        Product(config: config)
    }
}

let builder = Builder(config: .default)
let product = builder.build()
// builder is now invalid — compiler error if used
swift
struct Builder {
    var config: Configuration

    // 消耗self — 调用后builder不再可用
    consuming func build() -> Product {
        Product(config: config)
    }
}

let builder = Builder(config: .default)
let product = builder.build()
// builder现在已无效 — 使用会触发编译器错误

Pattern 3: Explicit Copy in Borrowing

模式3:borrowing中的显式拷贝

With
borrowing
, copies must be explicit:
swift
func store(_ value: borrowing LargeValue) {
    // ❌ Error: Cannot implicitly copy borrowing parameter
    self.cached = value

    // ✅ Explicit copy
    self.cached = copy value
}
使用
borrowing
时,必须显式调用拷贝:
swift
func store(_ value: borrowing LargeValue) {
    // ❌ 错误:无法隐式拷贝borrowing参数
    self.cached = value

    // ✅ 显式拷贝
    self.cached = copy value
}

Pattern 4: Consume Operator

模式4:consume操作符

Transfer ownership explicitly:
swift
let data = loadLargeData()
process(consume data)
// data is now invalid — compiler prevents use
显式转移所有权:
swift
let data = loadLargeData()
process(consume data)
// data现在已无效 — 编译器会阻止使用

Pattern 5: Noncopyable Type

模式5:非可复制类型

For
~Copyable
types, ownership modifiers are required:
swift
struct FileHandle: ~Copyable {
    private let fd: Int32

    init(path: String) throws {
        fd = open(path, O_RDONLY)
        guard fd >= 0 else { throw POSIXError.errno }
    }

    borrowing func read(count: Int) -> Data {
        // Read without consuming handle
        var buffer = [UInt8](repeating: 0, count: count)
        _ = Darwin.read(fd, &buffer, count)
        return Data(buffer)
    }

    consuming func close() {
        Darwin.close(fd)
        // Handle consumed — can't use after close()
    }

    deinit {
        Darwin.close(fd)
    }
}

// Usage
let file = try FileHandle(path: "/tmp/data.txt")
let data = file.read(count: 1024)  // borrowing
file.close()  // consuming — file invalidated
对于
~Copyable
类型,必须使用所有权修饰符:
swift
struct FileHandle: ~Copyable {
    private let fd: Int32

    init(path: String) throws {
        fd = open(path, O_RDONLY)
        guard fd >= 0 else { throw POSIXError.errno }
    }

    borrowing func read(count: Int) -> Data {
        // 不消耗句柄进行读取
        var buffer = [UInt8](repeating: 0, count: count)
        _ = Darwin.read(fd, &buffer, count)
        return Data(buffer)
    }

    consuming func close() {
        Darwin.close(fd)
        // 句柄已被消耗 — close()后无法使用
    }

    deinit {
        Darwin.close(fd)
    }
}

// 使用示例
let file = try FileHandle(path: "/tmp/data.txt")
let data = file.read(count: 1024)  // borrowing
file.close()  // consuming — file已失效

Pattern 6: Reducing ARC Traffic

模式6:减少ARC开销

swift
class ExpensiveObject { /* ... */ }

// ❌ Default: May retain/release
func inspect(_ obj: ExpensiveObject) -> String {
    obj.description
}

// ✅ Borrowing: No ARC traffic
func inspect(_ obj: borrowing ExpensiveObject) -> String {
    obj.description
}
swift
class ExpensiveObject { /* ... */ }

// ❌ 默认行为:可能会触发保留/释放操作
func inspect(_ obj: ExpensiveObject) -> String {
    obj.description
}

// ✅ Borrowing:无ARC开销
func inspect(_ obj: borrowing ExpensiveObject) -> String {
    obj.description
}

Pattern 7: Consuming Method on Self

模式7:作用于Self的消耗型方法

swift
struct Transaction {
    var amount: Decimal
    var recipient: String

    // After commit, transaction is consumed
    consuming func commit() async throws {
        try await sendToServer(self)
        // self consumed — can't modify or reuse
    }
}
swift
struct Transaction {
    var amount: Decimal
    var recipient: String

    // 提交后,transaction被消耗
    consuming func commit() async throws {
        try await sendToServer(self)
        // self已被消耗 — 无法修改或复用
    }
}

Common Mistakes

常见错误

Mistake 1: Over-Optimizing Small Types

错误1:过度优化小型类型

swift
// ❌ Unnecessary — Int is trivially copyable
func add(_ a: borrowing Int, _ b: borrowing Int) -> Int {
    a + b
}

// ✅ Let compiler optimize
func add(_ a: Int, _ b: Int) -> Int {
    a + b
}
swift
// ❌ 无必要 — Int是可 trivial 拷贝的类型
func add(_ a: borrowing Int, _ b: borrowing Int) -> Int {
    a + b
}

// ✅ 交由编译器优化
func add(_ a: Int, _ b: Int) -> Int {
    a + b
}

Mistake 2: Forgetting Explicit Copy

错误2:忘记显式拷贝

swift
func cache(_ value: borrowing LargeValue) {
    // ❌ Compile error
    self.values.append(value)

    // ✅ Explicit copy required
    self.values.append(copy value)
}
swift
func cache(_ value: borrowing LargeValue) {
    // ❌ 编译错误
    self.values.append(value)

    // ✅ 需要显式拷贝
    self.values.append(copy value)
}

Mistake 3: Consuming When Borrowing Suffices

错误3:在borrowing足够的场景下使用consuming

swift
// ❌ Consumes unnecessarily — caller loses access
func validate(_ data: consuming Data) -> Bool {
    data.count > 0
}

// ✅ Borrow for read-only
func validate(_ data: borrowing Data) -> Bool {
    data.count > 0
}
swift
// ❌ 不必要的消耗 — 调用者失去访问权限
func validate(_ data: consuming Data) -> Bool {
    data.count > 0
}

// ✅ 只读场景使用borrowing
func validate(_ data: borrowing Data) -> Bool {
    data.count > 0
}

Performance Considerations

性能考量

When Ownership Modifiers Help

所有权修饰符的有效场景

  • Large structs (arrays, dictionaries, custom value types)
  • High-frequency function calls in tight loops
  • Reference types where ARC traffic is measurable
  • Noncopyable types (required, not optional)
  • 大型结构体(数组、字典、自定义值类型)
  • 循环中的高频函数调用
  • ARC开销可观测的引用类型
  • 非可复制类型(必填,非可选)

When to Skip

无需使用的场景

  • Default behavior is almost always optimal
  • Small value types (primitives, small structs)
  • Code where profiling shows no benefit
  • API stability concerns (modifiers affect ABI)
  • 默认行为几乎总是最优的
  • 小型值类型(基本类型、小型结构体)
  • 性能分析显示无收益的代码
  • 关注API稳定性的场景(修饰符会影响ABI)

Decision Tree

决策树

Need explicit ownership?
├─ Working with ~Copyable type?
│  └─ Yes → Required (borrowing/consuming)
├─ Large value type passed frequently?
│  ├─ Read-only? → borrowing
│  └─ Final use? → consuming
├─ ARC traffic visible in profiler?
│  ├─ Read-only? → borrowing
│  └─ Transferring ownership? → consuming
└─ Otherwise → Let compiler choose
是否需要显式所有权?
├─ 是否处理~Copyable类型?
│  └─ 是 → 必须使用(borrowing/consuming)
├─ 是否频繁传递大型值类型?
│  ├─ 只读?→ 使用borrowing
│  └─ 最终使用?→ 使用consuming
├─ 性能分析中ARC开销可见?
│  ├─ 只读?→ 使用borrowing
│  └─ 转移所有权?→ 使用consuming
└─ 其他情况 → 交由编译器选择

Resources

参考资料

Swift Evolution: SE-0377
WWDC: 2024-10170
Skills: axiom-swift-performance, axiom-swift-concurrency
Swift Evolution: SE-0377
WWDC: 2024-10170
相关技能: axiom-swift-performance, axiom-swift-concurrency