Swiftのポインタの変換について試す

概要

moaible-swift-memo.hateblo.jp

  • SwiftのポインタにおいてUnsafeMutablePointerはUnsafePointerとして関数の引数に渡すことができる
    • これは暗黙のアップキャストによるものらしい
    • これだけ見た単純な理解だとMutableなポインタはImmutableなポインタ型として振る舞える?
  • 上記を検証したい

Swiftのポインタの仕様

qiita.com

おもちさんの記事を見ると基本のポインタ型としてMutableとImmutableでペアとして以下のようにまとめられそう

Mutable Immutable
UnsafeMutablePointer<T> UnsafePointer<T>
UnsafeMutableRawPointer UnsafeRawPointer
UnsafeMutableBufferPointer<T> UnsafeBufferPointer<T>
UnsafeMutableRawBufferPointer UnsafeRawBufferPointer

つまり冒頭の話通りであればMutableからImmutableなポインタに明示的に変換せずとも関数に引数として渡すことができるはず

検証

UnsafePointer

UnsafePointer - Swift Standard Library | Apple Developer Documentation

var n = 123
let np = UnsafeMutablePointer(&n)

func show(_ p: UnsafePointer<Int>) {
    print(p.pointee)
}

show(np) // -> 123

UnsafeRawPointer

UnsafeRawPointer - Swift Standard Library | Apple Developer Documentation

let np = UnsafeMutableRawPointer.allocate(bytes: 4, alignedTo: 1)
np.storeBytes(of: 0xFFFF_FFFF, as: UInt32.self)

func show(_ p: UnsafeRawPointer) {
    print(p.load(as: UInt8.self))
}

show(np) // -> 255

UnsafeBufferPointer

UnsafeBufferPointer - Swift Standard Library | Apple Developer Documentation

UnsafeMutableBufferPointer -> UnsafeBufferPointerは暗黙のアップキャストしない模様

型まで厳密に見てるという結果に

var n = 456
let np = UnsafeMutableBufferPointer(start: &n, count: 1)

func showFirst<T>(_ p: UnsafeBufferPointer<T>) {
    if let p0 = p.first {
        print("\(p0)")
    }
}

showFirst(np) // -> error: cannot convert value of type 'UnsafeMutableBufferPointer<Int>' to expected argument type 'UnsafeBufferPointer<_>'

immutableなら想定通りに挙動

var n = 456
let np = UnsafeBufferPointer(start: &n, count: 1)

func showFirst<T>(_ p: UnsafeBufferPointer<T>) {
    if let p0 = p.first {
        print("\(p0)")
    }
}

showFirst(np) // -> 456

UnsafeRawBufferPointer

UnsafeRawBufferPointer - Swift Standard Library | Apple Developer Documentation

UnsafeMutableRawBufferPointer -> UnsafeRawBufferPointerもUnsafeMutableBufferPointerと同じく、

暗黙のアップキャストは発生せず

var n = 256
let np = UnsafeMutableBufferPointer(start: &n, count: 1)
let mnp = UnsafeMutableRawBufferPointer(np)


func showFirst(_ p: UnsafeRawBufferPointer) {
    if let p0 = p.first {
        print("\(p0)")
    }
}

showFirst(mnp) // -> error: cannot convert value of type 'UnsafeMutableRawBufferPointer' to expected argument type 'UnsafeRawBufferPointer'

immutableなら想定通りは同一

var n = 256
let np = UnsafeMutableBufferPointer(start: &n, count: 1)
let mnp = UnsafeRawBufferPointer(np)

func showFirst(_ p: UnsafeRawBufferPointer) {
    if let p0 = p.first {
        print("\(p0)")
    }
}

showFirst(mnp) // -> 0

まとめ

つまり検証として暗黙的アップキャストが行われるのは

Mutable Immutable 暗黙的アップキャスト
UnsafeMutablePointer<T> UnsafePointer<T> OK
UnsafeMutableRawPointer UnsafeRawPointer OK
UnsafeMutableBufferPointer<T> UnsafeBufferPointer<T> NG
UnsafeMutableRawBufferPointer UnsafeRawBufferPointer NG