Unwrapping value with `guard let` sometimes does not work with result from data.withUnsafeBytes { $0.pointee }

Hello, I have problem with fallowing code, where I want to unwrap value returned by .withUnsafeBytes(_:). Sometimes (with exactly same data) it unwraps property properly and sometimes don't.

    func testGuardLet() {
        let data = Data(bytes: [7, 0, UInt8.min])
        let sizeData = data.subdata(in: 0 ..< 2)
        let size: Int? = sizeData.withUnsafeBytes { $0.pointee }
        guard let unwrappedSize = size else {
            print("failure: \(String(describing: size))")
            XCTFail()
            return
        }
        print("success: \(unwrappedSize)")
    }

In console I can see log:

2018-10-25 16:32:19.497540+0200 GuardBug[90576:11351167] failure: Optional(7)

Is this some known bug of swift? How can I fix it?

You've made a Data that's three bytes long, then you limit it to two bytes, then you try to read an Int? out of it, which is nine bytes and also isn't guaranteed to have any particular representation. What are you really trying to do?

1 Like

I posted whole code to: ios - Unwrapping optional value (returned by data.withUnsafeBytes(_:)) sometimes does not work with guard let - Stack Overflow

Example code is just narrow way how to reproduce the bug. In nutshell I need to concatenate multiple chunks of data to one piece and first two bytes of first chunk represents size of concatenated data.

The $0 argument to withUnsafeBytes specifies what type you want to read from the Data; you probably want that to be a UInt16:

let size = sizeData.withUnsafeBytes {
  (pointer: UnsafePointer<UInt16>) in
  pointer.pointee
}

(If this is something you're serializing, you may also need to care about being big-endian or little-endian!)

But it is not possible to compile because of error: 'UnsafePointer<UInt16>' is not convertible to 'UnsafePointer<_>'

:-/ "It works for me" with Xcode 10. Here's the test case I used:

import Foundation
func test(sizeData: Data) {
  let size = sizeData.withUnsafeBytes {
    (pointer: UnsafePointer<UInt16>) in
    pointer.pointee
  }
}

If you can reproduce your issue in a self-contained test case, please file a bug about that error message! Most likely the actual problem is something else.

(Note that I stopped specifying the type of size. There's no reason for it to be Optional, but it also can't be an Int because you're only reading two bytes. You'll have to convert the result to Int yourself.)

Yes, it works, my bad, I was trying let size: Int16? = sizeData.withUnsafeBytes { (pointer: UnsafePointer<UInt16>) in pointer.pointee }. Thank you