[swift-evolution] Making pointer nullability explicit (using Optional)

Russ Bishop xenadu at gmail.com
Fri Mar 18 16:54:25 CDT 2016


> On Mar 18, 2016, at 9:49 AM, Jordan Rose <jordan_rose at apple.com> wrote:
> 
> 
>> On Mar 17, 2016, at 21:08 , Russ Bishop <xenadu at gmail.com <mailto:xenadu at gmail.com>> wrote:
>> 
>> I’m very much +1 on this idea.
>> 
>>> On Mar 17, 2016, at 6:59 PM, Jordan Rose via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>  <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#open-issue-unsafebufferpointer>Open Issue: UnsafeBufferPointer
>>> 
>>> The type UnsafeBufferPointer represents a bounded typed memory region with no ownership or lifetime semantics; it is logically a bare typed pointer (its baseAddress) and a length (count). For a buffer with 0 elements, however, there's no need to provide the address of allocated memory, since it can't be read from. Previously this case would be represented as a nil base address and a count of 0.
>>> 
>>> With optional pointers, this now imposes a cost on clients that want to access the base address: they need to consider the nil case explicitly, where previously they wouldn't have had to. There are several possibilities here, each with their own possible implementations:
>>> 
>>> Like UnsafePointer, UnsafeBufferPointer should always have a valid base address, even when the count is 0. An UnsafeBufferPointer with a potentially-nil base address should be optional.
>>> 
>>> UnsafeBufferPointer's initializer accepts an optional pointer and becomes failable, returning nil if the input pointer is nil.
>>> 
>>> UnsafeBufferPointer's initializer accepts an optional pointer and synthesizes a non-null aligned pointer value if given nil as a base address.
>>> 
>>> UnsafeBufferPointer's initializer only accepts non-optional pointers. Clients such as withUnsafeBufferPointermust synthesize a non-null aligned pointer value if they do not have a valid pointer to provide.
>>> 
>>> UnsafeBufferPointer's initializer only accepts non-optional pointers. Clients using withUnsafeBufferPointermust handle a nil buffer.
>>> 
>>> UnsafeBufferPointer should allow nil base addresses, i.e. the baseAddress property will be optional. Clients will need to handle this case explicitly.
>>> 
>>> UnsafeBufferPointer's initializer accepts an optional pointer, but no other changes are made.
>>> 
>>> UnsafeBufferPointer's initializer accepts an optional pointer. Additionally, any buffers initialized with a count of 0 will be canonicalized to having a base address of nil.
>>> 
>>> I'm currently leaning towards option (2i). Clients that expect a pointer and length probably shouldn't require the pointer to be non-null, but if they do then perhaps there's a reason for it. It's also the least work.
>>> 
>>> Chris (Lattner) is leaning towards option (1ii), which treats UnsafeBufferPointer similar to UnsafePointer while not penalizing the common case of withUnsafeBufferPointer.
>> What’s the use of an UnsafeBufferPointer with zero count? Semantically that is making a claim that it can’t back up (“I have allocated memory at location X” which isn’t compatible with the idea of “zero count/size").
>> 
>> Without knowing more context I’d strongly favor (1i). If an array is empty the natural expectation for withUnsafeBufferPointer is you get UnsafeBufferPointer<Element>? = nil, which follows the behavior of the rest of the language and things like guard let make it trivial to handle properly. If someone really has a problem with it they can add ifUnsafeBufferPointer() that returns a non-optional pointer and skips executing the closure if the Array is empty (which is the behavior of your standard for loop).
> 
> The important use case here is that "array.withUnsafeBufferPointer" should always do something (i.e. it usually can't just skip the closure), and it turns out it's easiest if the zero-element case is treated the same as everything else. When converting over the standard library I found that very few of them wanted to do something different in the zero-element case, and then it would be bad to force Array to allocate memory just to not use it. That is, there aren't actually any clients interested in knowing whether the base address is valid, and all of the ones that do have to think about it (because they use it directly) aren't getting any value out of it.
> 
> Jordan

Does optional chaining (ptr?.count ?? 0) or the guard check (guard let ptr = ptr else { return }) impose a performance (or cognitive) burden here? I’m OK with (2i), it just seems less Swift-ish than (1i). 

I don’t use UnsafeBufferPointer a lot so I’ll happily live with whatever the choice is.

Russ



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160318/33e8d161/attachment.html>


More information about the swift-evolution mailing list