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

Russ Bishop xenadu at gmail.com
Thu Mar 17 23:08:53 CDT 2016


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> wrote:

> I consider this a bit more difficult to understand than the original code, at least at a glance. We should therefore add new initializers of the following form:
> 
> init?<OtherPointee>(_ otherPointer: UnsafePointer<OtherPointee>?) {
>   guard let nonnullPointer = otherPointer else {
>     return nil
>   }
>   self.init(nonnullPointer)
> }
> The body is for explanation purposes only; we'll make sure the actual implementation does not require an extra comparison.
> 
> (This would need to be an overload rather than replacing the previous initializer because the "non-null-ness" should be preserved through the type conversion.)
> 
> The alternative is to leave this initializer out, and require the nil case to be explicitly handled or mapped away.
> 
> 

This should definitely be included. Everyone is used to “casting” by calling the appropriate initializers, and UnsafePointer<SpecificType>(someUnsafeVoidPointer) maps directly to what a C/Objective-C programmer would expect.


>  <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).


Russ



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160317/5dfa878f/attachment.html>


More information about the swift-evolution mailing list