Search code examples
swiftunsafe-pointers

UnsafePointer<Int8> not working


The following unit test always fails as some UnsafePointer equal some other. This behavior I observe for at least 3 strings. I'm using Xcode Version 9.1 (9B55) on High Sierra Version 10.13.1 (17B48).

func testUnsafePointer() {
    let a = "aaa"
    let b = "bbb"
    let c = "ccc"

    let ua = UnsafePointer<Int8>(a)
    let ub = UnsafePointer<Int8>(b)
    let uc = UnsafePointer<Int8>(c)

    print("a: '\(a)', ua: '\(ua)'")
    print("b: '\(b)', ub: '\(ub)'")
    print("c: '\(c)', uc: '\(uc)'")

    XCTAssertTrue(ua != ub)
    XCTAssertTrue(ub != uc)
    XCTAssertTrue(uc != ua)
}

The printout looks similar to this

a: 'aaa', ua: '0x000060c000056a30'
b: 'bbb', ub: '0x00006080000530d0'
c: 'ccc', uc: '0x00006080000530d0'

NB. in the swift playground everything looks ok.


Solution

  • In

    let ua = UnsafePointer<Int8>(a)
    

    the

    init(_ other: UnsafePointer<Pointee>)
    

    initializer of UnsafePointer is called. I.e. you are passing a Swift String to a function taking a UnsafePointer<Int8> argument. In that case a temporary C string representation is created and passed to the function, compare String value to UnsafePointer<UInt8> function parameter behavior.

    That temporary C string is only valid for the duration of the function call, and on return, it is not guaranteed that the memory pointed to by ua still contains that C string (or any valid data). In addition,

    let ua = UnsafePointer<Int8>(a)
    let ub = UnsafePointer<Int8>(b)
    let uc = UnsafePointer<Int8>(c)
    

    can use the same storage for the temporary C string, in that case the pointers would be equal, as you observed.


    If your intention is to call a C function taking const char * arguments, then you can simply pass the Swift strings. The compiler will insert the necessary code to create C string representations (which are again valid for the duration of the function call), as explained in String value to UnsafePointer<UInt8> function parameter behavior:

    let a = "aaa"
    let b = "bbb"
    let c = "ccc"
    
    cfunc(a, b, c)