4
var a = [1,2,3]
let ptr1 = UnsafeMutablePointer<Int>(&a[0]) //works fine

let index = 0
let ptr2 = UnsafeMutablePointer<Int>(&a[index]) //compiler throws error

error: cannot invoke initializer for type UnsafeMutablePointer<Int> with an argument list of type (inout Int)

Why the latter one doesn't compile? Is there anything I am missing here?


I wanted to do like a below snippet.

class Holder {
    var numbers: [Int] = [1,2,3,4]
    var modifier: Modifier

    init(index: Int) {
       self.modifier = Modifier(UnsafeMutablePointer(&self.numbers) + index)
    }

}

class Modifer {
  var ptr: UnsafeMutablePointer<Int>

  init(_ ptr: UnsafeMutablePointer<Int>) {
     self.ptr = ptr
  }

  func change(to: Int) {
     self.ptr.pointee = to
    // expected the change to be reflected in numbers array
    // but as Rob Napier said it became invalid and throws EXC_BAD_ACCESS
  }
}

How can I achieve the above expected result?

jblixr
  • 1,345
  • 13
  • 34

1 Answers1

9

Note that neither of these is a valid way to create an UnsafeMutablePointer. Swift is free to deallocate a immediately after the last time it is referenced, so by the time you use these pointers, they may be invalid. The tool you want is a.withUnsafeMutableBufferPointer.

That said, the correct syntax here is:

let ptr2 = UnsafeMutablePointer(&a) + index

Looking at your updated code, there's no way for this to make sense on Array. I think you're assuming that Arrays are reference types. They're value types. There's no way to change a piece of numbers. Any change to it replaces the entire array with a completely different array. Swift has some clever copy-on-write tricks to make this efficient, and in practice it may not actually replace the entire array, but you should program as though it did. You should consider the following line:

array[1] = 2

to be equivalent to:

array = <a new array that is identical, but element 1 has been replaced by 2>

This means that pointers into Array are meaningless outside of very controlled situations (such as inside a withUnsafeMutableBufferPointer block).

What you want is something more like this:

class Holder {
    var numbers: [Int] = [1,2,3,4]
    private(set) var modifier: ((Int) -> ())! // ! is an artifact of capturing self in init

    init(index: Int) {
        self.modifier = { [weak self] in self?.numbers[index] = $0 }
    }
}

let holder = Holder(index: 2)
holder.numbers  // [1, 2, 3, 4]
holder.modifier(0)
holder.numbers  // [1, 2, 0, 4]
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • How to use `withUnsafeMutableBufferPointer`? When I try it throws error: cannot use mutating member on immutable value: 'a' is immutable. – jblixr Feb 28 '18 at 09:09
  • It depends on what you want to do with the contents. Inside the block, you have to use the buffer pointer provided. You can't access `a` directly. What's the goal? – Rob Napier Feb 28 '18 at 17:25
  • I have updated the question to minimally describe what I actually tried to achieve. In my original case the numbers array is a array of structs – jblixr Feb 28 '18 at 17:54
  • Updated the answer. You don't want pointers for this. You want closures. – Rob Napier Feb 28 '18 at 21:48