Alnitak
Alnitak

Reputation: 339786

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

Upvotes: 1

Views: 168

Answers (1)

Martin R
Martin R

Reputation: 539685

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

Upvotes: 2

Related Questions