Swift Language – Struct vs Class

optimizationswift-language

One of the stated reasons that structs can be more performant than classes is that no ARC is needed for structs. But suppose we have the following struct in swift:

struct Point {
   var x:Float
   var y:Float
   mutating func scale(_ a:Float){
      x *= a
      y *= a
   } 
}

var p1 = Point(x:1, y:1)
var p2 = p1 //p1 and p2 point to the same data internally
p1.scale(2) //copy on mutate, p1 and p2 now have distinct copies

Now I believe that various copies of a struct will actually point to the same data on the stack until a mutation forces a copy. This means either we have to keep track of how many objects reference a given memory address on the stack so we know if a mutation must form a copy, or we simply form a copy on every mutation. The former seems inefficient and the latter seems identical to ARC. What am I missing?

EDIT: I understand the difference between value semantics and reference semantics. But as an optimization swift does not actually create a new copy of the data until a mutation is made. This avoids unnecessary copies. See https://www.hackingwithswift.com/example-code/language/what-is-copy-on-write . But because of this I am not sure how they can avoid some form of ARC. I noticed that this article claims that copy on write is only done for arrays and dictionaries. Not sure if that is true but if so then my questions still stands for arrays.

Best Answer

The swift struct only needs to know if it's data is uniquely referenced or not, and not needing a full-fledged ARC. For example, on the line

var p2 = p1

the compiler could theoretically just flip a uniquely-referenced flag of p1 from off to on. Then, when a write is made, the compiler knows to copy.

Suppose p2 is deallocated before this happens. Now, the copy is redundant, but the compiler doesn't know, so it still makes the copy. That's why the uniquely-referenced approach isn't a perfect approximation, in actuality it falls between full ARC and always copying.

Note: the swift compiler doesn't actually work this way - based on @Alexander's comment and this answer, the copy on write behavior is implemented in swift code, not as a compiler optimization. On user-defined structs, it simply copies every time.

As to your point about efficiency: yes, technically copying every time is less efficient. When you have a bunch of copying and mutating behaviors, at some point it becomes more efficient just to switch to classes. That's why swift gives you both options.

Related Topic