- Proposal: SE-0205
- Authors: Joe Groff
- Review Manager: Ben Cohen
- Status: Implemented (Swift 4.2)
- Implementation: apple/swift#15608
We propose to extend the toplevel withUnsafePointer(to:_:)
and
withUnsafeBytes(of:_:)
functions to work with read-only values.
Swift-evolution thread: withUnsafePointer
and withUnsafeBytes
for immutable arguments
withUnsafePointer
and withUnsafeBytes
provide temporary scoped access to
the in-memory representation of variables and properties via pointers. They
currently only accept inout
arguments, which makes working with the
in-memory representation of immutable values more awkward and inefficient
than it ought to be, requiring a copy:
let x = 1 + 2
var x2 = x
withUnsafeBytes(of: &x2) { ... }
Even for inout
arguments, the semantics of withUnsafeBytes
and
withUnsafePointer
are extremely narrow: the pointer cannot be used after
the closure returns, cannot be mutably aliased, and is not guaranteed to
have a stable identity across withUnsafe*
applications to the same
property. There's therefore no fundamental reason these operations couldn't
also be offered on arbitrary read-only variables or temporary values, since
memory does not need to be persisted for longer than a call. It is a
frequent request to extend these APIs to work with read-only values.
We add overloads of withUnsafeBytes
and withUnsafePointer
that accept
their to
/of
argument by value (by shared borrow, when we get moveonly
types).
The following top-level functions are added to the standard library:
public func withUnsafePointer<T, Result>(
to value: /*borrowed*/ T,
_ body: (UnsafePointer<T>) throws -> Result
) rethrows -> Result
public func withUnsafeBytes<T, Result>(
of value: /*borrowed*/ T,
_ body: (UnsafeRawBufferPointer) throws -> Result
) rethrows -> Result
Like their inout
-taking siblings, each method produces a pointer to
the in-memory representation of its value
argument and invokes the body
closure with the pointer. The pointer is only valid for the duration of body
.
It is undefined behavior to write through the pointer or access it after
body
returns. It is unspecified whether taking a pointer to the same
variable or property produces a consistent pointer value across different
withUnsafePointer
/Bytes
calls.
We do not propose removing the inout
-taking forms of withUnsafePointer
and withUnsafeBytes
, both for source compatibility, and because in Swift
today, using inout
is still the only reliably way to assert exclusive
access to storage and suppress implicit copying. Optimized Swift code
ought to in principle avoid copying when withUnsafePointer(to: x)
is passed
a stored let
property x
that is known to be immutable, and with moveonly
types it would be possible to statically guarantee this, but in Swift today
this cannot be guaranteed.
Existing code can continue to use the inout
-taking forms without modification.
This is a purely additive change to the standard library. In anticipation
of a future version of Swift adding moveonly types, we should ensure that
the implementations of withUnsafePointer
and withUnsafeBytes
takes their
arguments +0 so that they will be ABI-compatible with shared borrows of
moveonly types in the future.