Alnitak Alnitak - 4 months ago 12
Swift Question

Swift block syntax failure to infer type

Can anyone explain why this single line block with an implicit

return
compiles:

let r = withUnsafePointer(&msg) {
dn_expand(UnsafePointer($0), eomorig: UnsafePointer($0).advancedBy(msg.count), comp_dn: UnsafePointer($0).advancedBy(offset), exp_dn: &buf, length: buf.count)
}


but this version refactored where the only difference is to avoid the multiple calls to
UnsafePointer($0)
doesn't:

let s = withUnsafePointer(&msg) {
let p = UnsafePointer($0)
return dn_expand(p, eomorig: p.advancedBy(msg.count), comp_dn: p.advancedBy(offset), exp_dn: &buf, length: buf.count)
}


with error message:


Cannot convert value of type 'inout [UInt8]' (aka 'inout Array<UInt8>') to expected argument type 'inout _'



The
dn_function
being called is just a trivial wrapper around
dn_expand
from
libresolv
:

public static func dn_expand(msg: UnsafePointer<UInt8>, eomorig: UnsafePointer<UInt8>, comp_dn: UnsafePointer<UInt8>, exp_dn: UnsafeMutablePointer<CChar>, length: Int) -> Int {
return Int(res_9_dn_expand(msg, eomorig, comp_dn, exp_dn, Int32(length)))
}

Answer

As already said in the comments, withUnsafePointer() is not the correct way to get a pointer to the element storage. It compiles, but gives unexpected results, as the following example demonstrates:

var msg: [UInt8] = [1, 2, 3, 4]

func foo(x: UnsafePointer<UInt8>) {
    print(x[0])
}

withUnsafePointer(&msg) {
    foo(UnsafePointer($0))
}

This prints "random" numbers, but not the expected 1. The correct way is to call the withUnsafeBufferPointer() method on the array:

msg.withUnsafeBufferPointer {
    foo($0.baseAddress)
}

In your case that would be

let r = msg.withUnsafeBufferPointer {
    dn_expand($0.baseAddress, eomorig: $0.baseAddress + $0.count, ...)
}

Here the return type of the closure is inferred automatically because it is a "single-expression" closure. If the closure contains more than one expression, you have to specify its type:

let r = msg.withUnsafeBufferPointer { bufPtr -> Int in
    let p = bufPtr.baseAddress
    return dn_expand(p, eomorig: p + msg.count, ...)
}

or let the compiler infer the return type from the context:

let r: Int = msg.withUnsafeBufferPointer { bufPtr in
    let p = bufPtr.baseAddress
    return dn_expand(p, eomorig: p + msg.count, ...)
}
Comments