Joe Joe - 4 months ago 9
Swift Question

Type conversion when using protocol in Swift

protocol Test{

var t: Int {set get}

}

struct A: Test{

var t: Int

}

struct B: Test{

var t: Int
var s: String

}

let a = A(t: 1)
let b = B(t: 2, s: "1")

let c = a as Test

let d: [Test] = [a, b]

let e: [A] = [a, a]

let f = e as [Test]

let g = e as! [Test]


"let f = e as [Test], let g = e as! [Test]" always get wrong. I have a function which i have to send a [Test] into. Convert A to Test is easy to use as. But when it comes to Array, I have to traversal all the data. Is there any good way to convert [A] to [Test].

Answer

Code Different's answer is right, but it's also important to understand why you can't just reinterpret the data without something doing an O(n) conversion to walk though and wrap each element in a box.

[A] is an array with elements the size of A. So, more or less, A[1] is at memory location A[0] + sizeof(A) (this isn't exactly true, but pretend it is; it'll make things simpler).

[Test] has to be able to hold anything that conforms to the protocol. It might be the size of A, it might be the size of B, which is larger. To achieve that, it creates a box that holds "something that conforms to Test". The box is of a known size, and it may have to heap-allocate the thing it points to (or maybe not; depends), but it ain't going to be the same size as both A and B, so the layout of [Test] in memory has to be different than the layout of [A] or [B].

Now as [Test] could do that O(n) walk for you and force a copy right then. Or it delay the copy until the first time you modified something, but it would be much more complicated and it would bury an O(n) (possibly expensive, possibly memory allocating) event inside an as which feels like it should be O(1) and allocate no memory. So there's reasons not to do that.

Some day they may do it anyway to make things simpler on the caller, but there's a cost in both performance and complexity, and so it doesn't do it today and you need to do the map yourself.

If you want a much deeper dive into this stuff, with more details on how this box works (and less "pretend it's simpler than it is"), see Understanding Swift Performance from WWDC 2016.