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)
The problem with this one is that T can be deduced based on type
context. I think we ought to move away from that for operations like
this one.
Option (2) UnsafePointer<T>(_: UnsafeRawPointer, to: T.self)
I think you mean T.Type, not T.self, because this looks like a declaration.
To evaluate, you have to look at the use-site:
let p = UnsafePointer(r, to: Int.self)
I don't find “to” to be descriptive enough. Maybe
let p = UnsafePointer(r, pointee: Int.self)
is better. But I hate that the language doesn't give us a way to say
“don't deduce generic parameters here.” This is the only syntax that
feels right, IMO:
let p = UnsafePointer<Int>(r)
Option (3) UnsafeRawPointer.unsafeCast<T>(to: T.Type) ->
UnsafePointer<T>
r.unsafeCast(to: Int.self)
I don't see adding “unsafe” to the name of the operation as adding
anything. It isn't any more unsafe than other UnsafeRawPointer
operations. Also, it reads like we're casting the raw pointer to an
Int, rather than to an UnsafePointer<Int>. Also, how do you get an
UnsafeMutablePointer?
Option (4) unsafeCast(rawPointer: UnsafeRawPointer, to: T.self) ->
UnsafePointer<T>
This one won't read correctly for the same reasons as #3.
r.cast(to: UnsafePointer<Int>.self)
works better for me than any of the alternatives given our inability to
get the One True Syntax.
---
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).
Huh? I'm confused here. What you wrote looks like it's intended to be
a regular method, in which case of course invoking it would require a raw
pointer and wouldn't force you to write UnsafeRawPointer out anywhere.
The only way it could force you to write UnsafeRawPointer would be if it
was a static method, but in that case it has too few arguments.
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`).
r ?? someExpressionUsing(r!)
best I can do.
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?
Yes!
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
Use sites! (yay)...
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))
foo(rawPtr.cast(to: UnsafePointer<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))
foo(UnsafeRawPointer(ptrB).cast(to: UnsafePointer<A>.self))
I don't believe in making these “double-hops” concise.
---
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))
nullableFoo(UnsafeRawPointer(ptrB)?.cast(to: UnsafePointer<A>.self))
You do the above with a failable init on UnsafeRawPointer that takes an
optional UnsafePointer.
---
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)
return UnsafeRawPointer(foo()).cast(to: UnsafePointer<B>.self)
IMO-ly y'rs,
···
on Fri Jun 24 2016, Andrew Trick <atrick-AT-apple.com> wrote:
On Jun 24, 2016, at 11:22 AM, Andrew Trick via swift-evolution >> <swift-evolution@swift.org> wrote:
On Jun 24, 2016, at 11:17 AM, L. Mihalkovic >>> <laurent.mihalkovic@gmail.com >>> <mailto:laurent.mihalkovic@gmail.com>> wrote:
--
-Dave