2

I'm looking at ways to create a DispatchData instance out of a Data instance. I started with a static function:

static func dispatchData(fromData data: Data) -> DispatchData {
    return data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) -> DispatchData in
        let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
        return DispatchData(bytes: bufferPtr)
    }
}

This works fine (and is coincidentally very similar to this answer). I then decided I'd like to add this as an initializer to DispatchData in an extension and wrote:

private extension DispatchData {
    init(data: Data) {
        data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in  // 1
            let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
            self.init(bytes: bufferPtr)
        }
    }
}

At this, the compiler balked on the line marked // 1:

Variable 'self.__wrapped' captured by a closure before being initialized

It makes sense — the compiler doesn't want self to be captured before I've delegated to init(bytes: UnsafeRawBufferPointer), since stored variables — like __wrapped, in this case — aren't yet initialized. But I can't see another way to get the UnsafeRawBufferPointer; I can't return it from data.withUnsafeBytes, per the documentation:

Warning

The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.

I know I can just go back to using a static function to achieve what I want, but I'd prefer it if there were a way to add this initializer. Is there a way to make this initializer work as intended?

ravron
  • 11,014
  • 2
  • 39
  • 66

1 Answers1

3

The problem with your extension is that although the compiler knows you're passing the closure as non-escaping argument to withUnsafeBytes(_:); it doesn't have any guarantee that it will be called once and only once (which is required for initialisation).

One simple solution is to, rather than chain to another initialiser, just construct a new instance of DispatchData yourself, and then assign this to self, thus completing initialisation:

import Foundation

private extension DispatchData {
  init(data: Data) {
    self = data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in
      DispatchData(bytes:
        UnsafeRawBufferPointer(start: typedPtr, count: data.count)
      )
    }
  }
}

Note also that there's an implicit conversion from UnsafePointer<UInt8> to UnsafeRawPointer? when passing it as an argument; so we don't need to wrap it in an initialiser call.

And in Swift 5, withUnsafeBytes gives you a UnsafeRawBufferPointer directly:

private extension DispatchData {
  init(data: Data) {
    self = data.withUnsafeBytes(DispatchData.init)
  }
}
Hamish
  • 78,605
  • 19
  • 187
  • 280
  • 1
    Ah, assignment to `self` in the initializer! A good trick, I'll have to remember that. For others' reference, information about implicit casting of between `*Pointer` types can be found [here](https://developer.apple.com/documentation/swift/unsaferawpointer). – ravron Sep 28 '17 at 15:43