Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a mutating struct function in swift create a new copy of self?

I like value semantics in swift but I am worried about the performance of mutating functions. Suppose we have the following struct

struct Point {
   var x = 0.0
   mutating func add(_ t:Double){
      x += t
   }
}

Now suppose we create a Point and mutate it as so:

var p = Point()
p.add(1)

Now does the existing struct in memory get mutated, or is self replaced with a new instance as in

self = Point(x:self.x+1)
like image 675
gloo Avatar asked Feb 23 '17 16:02

gloo


1 Answers

Now does the existing struct in memory get mutated, or is self replaced with a new instance

Conceptually, these two options are exactly the same. I'll use this example struct, which uses UInt8 instead of Double (because its bits are easier to visualize).

struct Point {
    var x: UInt8
    var y: UInt8

    mutating func add(x: UInt8){
       self.x += x
    }
}

and suppose I create a new instance of this struct:

var p = Point(x: 1, y: 2)

This statically allocates some memory on the stack. It'll look something like this:

00000000  00000001  00000010  00000000
<------^  ^------^  ^------^ ^----->
other    | self.x | self.y | other memory
          ^----------------^
          the p struct

Let's see what will happen in both situations when we call p.add(x: 3):

  1. The existing struct is mutated in-place:

    Our struct in memory will look like this:

    00000000  00000100  00000010  00000000
    <------^  ^------^  ^------^ ^----->
    other    | self.x | self.y | other memory
            ^----------------^
            the p struct
    
  2. Self is replaced with a new instance:

    Our struct in memory will look like this:

    00000000  00000100  00000010  00000000
    <------^  ^------^  ^------^ ^----->
    other    | self.x | self.y | other memory
            ^----------------^
            the p struct
    

Notice that there's no difference between the two scenarios. That's because assigning a new value to self causes in-place mutation. p is always the same two bytes of memory on the stack. Assigning self a new value to p will only replace the contents of those 2 bytes, but it'll still be the same two bytes.

Now there can be one difference between the two scenarios, and that deals with any possible side effects of the initializer. Suppose this is our struct, instead:

struct Point {
    var x: UInt8
    var y: UInt8

    init(x: UInt8, y: UInt8) {
        self.x = x
        self.y = y
        print("Init was run!")
    }

    mutating func add(x: UInt8){
       self.x += x
    }
}

When you run var p = Point(x: 1, y: 2), you'll see that Init was run! is printed (as expected). But when you run p.add(x: 3), you'll see that nothing further is printed. This tells us that the initializer is not anew.

like image 57
Alexander Avatar answered Oct 20 '22 10:10

Alexander