[swift-evolution] Pitch: Improved Swift pointers

Andrew Trick atrick at apple.com
Thu Jul 13 17:56:22 CDT 2017


> On Jul 12, 2017, at 12:16 PM, Taylor Swift via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Hi all, I’ve written up a proposal to modify the unsafe pointer API for greater consistency, safety, and ease of use.
> 
> ~~~
> 
> Swift currently offers two sets of pointer types — singular pointers such as UnsafeMutablePointer, and vector (buffer) pointers such as UnsafeMutableBufferPointer. This implies a natural separation of tasks the two kinds of pointers are meant to do. For example, buffer pointers implement Collection conformance, while singular pointers do not.
> 
> However, some aspects of the pointer design contradict these implied roles. It is possible to allocate an arbitrary number of instances from a type method on a singular pointer, but not from a buffer pointer. The result of such an operation returns a singular pointer, even though a buffer pointer would be more appropriate to capture the information about the number of instances allocated. It’s possible to subscript into a singular pointer, even though they are not real Collections. Some parts of the current design turn UnsafePointers into downright DangerousPointers, leading users to believe that they have allocated or freed memory when in fact, they have not.
> 
> This proposal seeks to iron out these inconsistencies, and offer a more convenient, more sensible, and less bug-prone API for Swift pointers.
> 
> <https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06 <https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06>>
> 
> ~~~
> 

Thanks for taking time to write this up.

General comments:

UnsafeBufferPointer is an API layer on top of UnsafePointer. The role
of UnsafeBufferPointer is direct memory access sans lifetime
management with Collection semantics. The role of UnsafePointer is
primarily C interop. Those C APIs should be wrapped in Swift APIs that
take UnsafeBufferPointer whenever the pointer represents a C array. I
suppose making UnsafePointer less convenient would push developers
toward UnsafeBufferPointer. I don't think that's worth outright
breaking source, but gradual deprecation of convenience methods, like
`susbscript` might be acceptable.

I have mixed feelings about stripping UnsafePointer of basic
functionality. Besides breaking source, doing that would be
inconsistent with its role as a lower API layer. The advantage would
just be descreasing API surface area and forcing developers to use a
higher-level API.

The additive changes you propose are fairly obvious. See [SR-3088]
UnsafeMutableBufferPointer doesn't have an allocating init.

I haven't wanted to waste review cycles on small additive
changes. It may make sense to batch them up into one coherent
proposal. Here are a few more to consider.

- [SR-3929] UnsafeBufferPointer should have init from mutable
- [SR-4340] UnsafeBufferPointer needs a withMemoryRebound method
- [SR-3087] No way to arbitrarily initialise an Array's storage

Point by point:

> drop the capacity parameter from UnsafeMutablePointer.allocate() and deallocate().

I do not agree with removing the capacity parameter and adding a
single-instance allocation API. UnsafePointer was not designed for
single instances, it was primarily designed for C-style arrays. I
don't see the value in providing a different unsafe API for single
vs. multiple values.

I agree the primary allocation API should be
UnsafeMutableBufferPointer.allocate(capacity:). There is an argument
to be made for removing UnsafeMutablePointer.allocate(capacity:)
entirely. But, as Michael Ilseman pointed out, that would involve
reevaluating several other members of the UnsafePointer API. I think
it's reasonable for UnsafePointer to retain all its functionality as a
lower level API.

I don't understand what is misleading about
UnsafePointer.deallocate(capacity:). It *is* inconvenienent for the
user to keep track of memory capacity. Presumably that was done so
either the implementation can move away from malloc/free or some sort
of memory tracking can be implemented on the standard library
side. Obviously, UnsafeBufferPointer.deallocate() would be cleaner in
most cases.

> add an allocate(count:) type method to UnsafeMutableBufferPointer

`capacity` should be used for allocating uninitialized memory not
`count`. `count` should only refer to a number of initialized objects!

> add a deallocate() instance method to UnsafeMutableBufferPointer

Yes, of course! I added a mention of that in SR-3088.

> remove subscripts from UnsafePointer and UnsafeMutablePointer

It's often more clear to perform arithmetic on C array indices rather
than pointers. That said, I'm happy to push developers to use
UnsafeBufferPointer whenever that have a known capacity. To me, this
is a question of whether the benefit of making a dangerous thing less
convenient is worth breaking source compatibility.

-Andy

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170713/90fc7e28/attachment.html>


More information about the swift-evolution mailing list