Swift Digest
SE-0114 | Swift Evolution

Updating Buffer “Value” Names to “Header” Names

Proposal
SE-0114
Authors
Erica Sadun
Review Manager
Chris Lattner
Status
Implemented (Swift 3.0)

01 何が問題だったのか

標準ライブラリには、参照カウント付きの可変長バッファを低レベルに扱うための ManagedBuffer / ManagedProtoBuffer / ManagedBufferPointer という型があります。これらは、バッファ本体(末尾に確保される可変長の要素領域)と、それに付随するメタ情報(長さ、容量など)を1つの割り当てにまとめて持つための仕組みで、Array など標準のコレクション型の基盤として使われています。

これらの型では、メタ情報を格納する領域のことを「value」と呼び、対応する API も value / Value という名前で揃えられていました。

// 旧 API のイメージ
class ManagedBuffer<Value, Element> {
    var value: Value
    // ...
    final func withUnsafeMutablePointerToValue<R>(
        _ body: (UnsafeMutablePointer<Value>) -> R
    ) -> R
}

しかし「value」という語はあまりに一般的で、「バッファ本体(要素列)の値」とも読めてしまい、実際の役割である「要素列の先頭に付いているヘッダ情報」を十分に表現できていませんでした。このため、API の意図が読み取りにくく、他の型やプロパティ名と混同されやすいという命名上の問題がありました。

02 どのように解決されるのか

ManagedBuffer / ManagedProtoBuffer / ManagedBufferPointer で使われていた value / Value という名前を、役割をより正確に表す header / Header に一括で置き換えます。バッファ本体(要素列)に付随するメタ情報であることが、名前から直接読み取れるようになります。

変更される名前

ジェネリックパラメータとメンバのそれぞれが、次のように改名されます。

  • ジェネリックパラメータ <Value, Element><Header, Element> になります。
  • プロパティ value: Valueheader: Header になります。
  • 内部的なポインタ関連の名前 _valuePointer / _valueOffset_headerPointer / _headerOffset に揃えられます。
  • withUnsafeMutablePointerToValuewithUnsafeMutablePointerToHeader になります。
  • 生成 API の create(minimumCapacity:initialValue:) にあたるものは、ヘッダの初期化を行うクロージャを受け取る create(minimumCapacity:makingHeaderWith:) に置き換えられます。makingHeaderWith を取るイニシャライザも同様に名前が変わります。
// 新 API のイメージ
class ManagedBuffer<Header, Element> {
    var header: Header
    // ...
    final func withUnsafeMutablePointerToHeader<R>(
        _ body: (UnsafeMutablePointer<Header>) -> R
    ) -> R

    final class func create(
        minimumCapacity: Int,
        makingHeaderWith factory: (ManagedProtoBuffer<Header, Element>) -> Header
    ) -> ManagedBuffer<Header, Element>
}

使い方のイメージ

既存の利用例も、valueheader に置き換えるだけで同じ意味のコードになります。

final class MyBuffer: ManagedBuffer<MyHeader, Int> {
    static func make(capacity: Int) -> MyBuffer {
        let buffer = MyBuffer.create(minimumCapacity: capacity) { _ in
            MyHeader(count: 0)
        }
        return unsafeDowncast(buffer, to: MyBuffer.self)
    }

    func appendingZero() {
        withUnsafeMutablePointerToHeader { headerPtr in
            withUnsafeMutablePointerToElements { elementsPtr in
                elementsPtr[headerPtr.pointee.count] = 0
                headerPtr.pointee.count += 1
            }
        }
    }
}

既存コードへの影響

value / Value を使っていた既存コードは、単純な fix-it でそのまま header / Header に置き換えられます。命名以外のセマンティクスや挙動は変わらないため、移行は機械的な置換のみで済みます。