Swift Digest
SE-0093 | Swift Evolution

Adding a public base property to slices

Proposal
SE-0093
Authors
Max Moiseev
Review Manager
Dave Abrahams
Status
Implemented (Swift 3.0)

01 何が問題だったのか

標準ライブラリの各種スライス型(SliceMutableRandomAccessSlice など)は、元となるコレクション(base collection)への参照を内部的に保持していました。しかし、その参照を外部から読み取る公開APIが存在しなかったため、スライスを使う側から元のコレクションに直接アクセスすることができませんでした。

効率的な実装を妨げる

この制約は、MutableCollection への適合時に特に問題となります。MutableCollection は、範囲指定で値を書き戻すための subscript を要求します。

subscript(bounds: Range<Index>) -> SubSequence { get set }

SubSequence として標準ライブラリのスライス型をそのまま使う場合、この setter の既定実装は _writeBackMutableSlice という汎用的な書き戻しアルゴリズムを利用します。forward collection では無難な実装ですが、連続メモリ上に要素を持つ配列のようなコレクションでは、本来であれば memcpy で一気にコピーするのが最も効率的です。

ところが、スライスからは元のコレクションを取り出す手段がなく、適合側でそのような最適化を書こうとしても、memcpy が利用できる具体的なバッファに辿り着けませんでした。内部的には情報を持っているのに、公開APIの不足が原因で、より速い実装を書けないという状況だったのです。

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

標準ライブラリが提供する各スライス型に、元のコレクションを読み取り専用で公開する base プロパティを追加します。ミュータブルなスライスとイミュータブルなスライスの両方に適用されます。

たとえば MutableRandomAccessSlice は次のようになります。

public struct MutableRandomAccessSlice<
  Base : protocol<RandomAccessIndexable, MutableIndexable>
> : RandomAccessCollection, MutableCollection {

  /// The underlying collection of the slice
  public var base: Base { get }
}

base を介して元のコレクションを取得できるようになるため、MutableCollection への適合側では、書き戻し処理を元コレクションのバッファに対する memcpy などで直接実装することが可能になります。既定の汎用アルゴリズムに頼らず、対象のコレクションに最適な経路でスライスの内容を反映できるようになる、というのが主な狙いです。

この変更は純粋に追加的なもので、既存のコードには影響しません。イミュータブルなスライスについては、内部に存在した _base プロパティを base にリネームして公開するという選択肢もありましたが、それだと追加のみでは済まなくなるため、新しい base プロパティを追加する形が採用されています。