1

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)))
}
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • What is the signature for `dn_expand`? – Scott Hunter Jul 12 '16 at 14:33
  • 2
    The compiler infers the type of a closure automatically only for *single-expression closures* or from the *context*, compare http://stackoverflow.com/a/34115788/1187415. – Martin R Jul 12 '16 at 14:37
  • @MartinR I suspected as such, but I cannot determine the correct explicit type myself – Alnitak Jul 12 '16 at 14:39
  • What type has `msg`? – Martin R Jul 12 '16 at 14:42
  • @MartinR it's a `[UInt8]` (per the error message) – Alnitak Jul 12 '16 at 14:43
  • How does the single expression version compile in the first place? If msg is of type `[UInt8]`, `$0` is of type `UnsafePointer<[UInt8]>`. Do you have an extension that defines an `UnsafePointer.init` from `UnsafePointer<[UInt8]>` to `UnsafePointer`? Anyhow, don't you wanna rather use `Array.withUnsafeBufferPointer`: `let s: Int = msg.withUnsafeBufferPointer { let p = $0.baseAddress; return ... }` – ingoem Jul 12 '16 at 15:33
  • @ingoem AFAIK you get "toll-free" bridging between `inout [UInt8]` and `UnsafePointer`, but I could be wrong... – Alnitak Jul 12 '16 at 15:34
  • Ah, it looks like `withUnsafeBufferPointer` may indeed be what I was missing! – Alnitak Jul 12 '16 at 15:38
  • @ingoem I perhaps meant bridging to `UnsafePointer<[UInt8]>` (NB `[...]`), rather than `UnsafePointer`. I've certainly been able to pass via `inout x : [Type]` via `&x` directly to `UnsafePointer` on other functions, though. – Alnitak Jul 12 '16 at 15:41
  • @Alnitak Yes, you can bridge between the two, but not in the way you originally wanted. You would be able to pass `&msg` to `dn_expand` directly without any of the `withUnsafe...` functions, but since you want to do pointer arithmetic with `advancedBy`, you have to use the `withUnsafe...` to get a handle to the pointer first. – ingoem Jul 12 '16 at 15:45
  • @ingoem right, and it's the array-specific buffer pointer methods that I was previously unaware of that seem to be the "right way" in this case. And yes, I was able to pass `&msg` for the first parameter, but not on the ones that required pointer arithmetic. – Alnitak Jul 12 '16 at 15:46
  • @ingoem if you'd like to write up an answer about using `withUnsafeBufferPointer` I'll happily accept :) – Alnitak Jul 12 '16 at 16:02
  • Thanks, Martin R beat me to it :) – ingoem Jul 12 '16 at 16:13

1 Answers1

2

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, ...)
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks (and also to @ingoem). For some reason I never managed to find `Array.withUnsafeBufferPointer()` before! :p – Alnitak Jul 12 '16 at 16:14