> On Jun 24, 2016, at 11:22 AM, Andrew Trick via swift-evolution 
> <[email protected]> wrote:
> 
>> On Jun 24, 2016, at 11:17 AM, L. Mihalkovic <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> I like the watch-what-you-wish-for warning of unsafeCast.
> 
> I’ll try porting stdlib to the “UnsafeRawPointer.unsafeCast(to: T.Type)” 
> syntax and see how bad it is.


I don't think there's a clear winner here. Let me enumerate some
options.

Option (1) UnsafePointer<T>(cast: UnsafeRawPointer)

Option (2) UnsafePointer<T>(_: UnsafeRawPointer, to: T.self)

Option (3) UnsafeRawPointer.unsafeCast<T>(to: T.Type) -> UnsafePointer<T>

Option (4) unsafeCast(rawPointer: UnsafeRawPointer, to: T.self) -> 
UnsafePointer<T>

---
Option (3) is the most explicit and searchable, and forces
UnsafeRawPointer to be spelled out in the conversion (unless you
already have a raw pointer). I like this because conceptually, you
need to cast to a raw pointer before casting to a new pointee
type, and casting a raw pointer to a typed pointer carries important
semantics beyond simply converting to a typed pointer. The main problem
with Option (3) is that optional raw pointers can't be converted
naturally (without using `map`).

Another thing I'm a little nervous about is confusing a cast of the
pointer value with a cast of the pointee type:

  `unsafeBitCast(rawPtr, to: Int.self)`

is very different from

  `rawPtr.unsafeCast(to: Int.self)`

Does this need to be clarified? If so, we can go back to the
`toPointee` label that I proposed earlier.

With that in mind, Option(4) is starting to look pretty good.

Examples:

---
Case 1: casting a raw pointer as an argument

func foo(_: UnsafePointer<A>)

let rawPtr = UnsafeRawPointer(...)

(1) foo(UnsafePointer(cast: rawPtr))

(2) foo(UnsafePointer(rawPtr, to: A.self))

(3) foo(rawPtr.unsafeCast(to: A.self))

(4) foo(unsafeCast(rawPointer: rawPtr, to: A.self))

---
Case 2: "recasting" a typed pointer argument

Note that typed pointer arguments are implicitly cast to raw pointer
arguments, so the conversion from PtrB to raw is implicit.

func foo(_: UnsafePointer<A>)

let ptrB = UnsafePointer<B>(...)

(1) foo(UnsafePointer(cast: ptrB))

(2) foo(UnsafePointer(ptrB, to: A.self))

(3) foo(UnsafeRawPointer(ptrB).unsafeCast(to: A.self))

(4) foo(unsafeCast(rawPointer: ptrB, to: A.self))

---
Case 3: Optional argument (only Option 3 is affected)

func nullableFoo(_: UnsafePointer<Int>?)

let ptrB: UnsafePointer<UInt>? = ...

(1) nullableFoo(UnsafePointer(cast: ptrB))

(2) nullableFoo(UnsafePointer(ptrB, to: A.self))

(3) nullableFoo(UnsafeRawPointer(ptrB).map { $0.unsafeCast(to: A.self) })

(4) nullableFoo(unsafeCast(rawPointer: ptrB, to: A.self))

---
Case 4: Return values

func foo() -> UnsafePointer<A>

func caller() -> UnsafePointer<B> { ...

(1) return UnsafePointer(cast: foo())

(2) return UnsafePointer(foo(), to: B.self)

(3) let rawPtr = UnsafeRawPointer(foo())
    return rawPtr.unsafeCast(to: B.self)

(4) return unsafeCast(rawPointer: foo(), to: B.self)

-Andy
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to