1

Please have a look at the following code and note the compiler error on the last line:

class C {
    var value: Int
    init( _ value: Int) { self.value = value }
}
let array1 = [C(1), C(2), C(3)]
array1[0].value = 4

struct S {
    var value: Int
}
let array2 = [S(value: 1), S(value: 2), S(value: 3)]
array2[0].value = 4 // Error: Cannot assign to property: 'array2' is a 'let' constant

From the compiler error I want to conclude that the item at index 0 is being read from array2, modified, and then written back to array2. What else could produce the result that there is an attempt to modify array2? But, if my reasoning is correct, then why does the same thing not happen with array1?

Hamish
  • 78,605
  • 19
  • 187
  • 280
Verticon
  • 2,419
  • 16
  • 34
  • Somewhat related: https://stackoverflow.com/q/38010936/2976878. The same thing doesn't happen with `array1` because `C` is a reference type; mutating `value` doesn't change the contents of the array, that's still just 3 references. – Hamish Dec 14 '17 at 08:57
  • @Hamish Thanks for responding. The distinction is subtle. How are value types stored in an array such that modifying the value results in modifying the array? Its odd, no? It is as though that when an array holds value types the array itself is somehow intimately entwined with the items that it is storing. – Verticon Dec 14 '17 at 16:53
  • @Hamish As I said originally: Is there a read -> modify -> write occurring? If yes then the write is what produces the modification of the array and is thus disallowed. – Verticon Dec 14 '17 at 17:02
  • I'm not sure I fully understand you; if you're referring to the fact that `Array` stores its contents indirectly (and so mutating the elements isn't technically a mutation of the array value itself), then you're quite correct, but are getting a little too bogged down in the technical details rather than looking at the semantics. `Array` has value semantics; a mutation of an element in the array is semantically equivalent to assigning a totally new array with the mutation applied (+ side effects).... – Hamish Dec 14 '17 at 17:18
  • ...If you could mutate an array's elements while it was declared as a `let`, `Array` would no longer have value semantics. `C` has reference semantics; so mutating one of its properties is not a mutation of its value (if we consider its value to be the reference to the instance). Therefore it's not a mutation of the array, so is allowed when the array is a `let`. – Hamish Dec 14 '17 at 17:18
  • Ah actually I think I see what you're getting at with "*Is there a read -> modify -> write occurring*" – Let's just consider a subscript with a getter and a setter for now. If that subscript 'returns' a value type, then yes, if you go to mutate that value type through the subscript, then the getter will be called, the value will be put into a temporary, that'll be mutated, and then the setter will be called with the new value. That doesn't happen for reference types though; only the getter is called, as the instance can be mutated through the reference. – Hamish Dec 14 '17 at 17:23
  • (`mutating` methods in protocol extensions are one exception to that though; they can actually mutate the reference). Although if you really want to dive into the technically details, `Array`'s `subscript(_:)` is a little bit special; it can return pointers to the array's elements (in storage) such that they can be mutated directly. This doesn't change the semantics though. – Hamish Dec 14 '17 at 17:24
  • @Hamish Thank you very much for your help. My fog finally lifted. I was thinking of the array as separate from what the array stores whereas as in fact the array IS what it stores. The defining question is: Will the operation alter the memory allocated to the array? – Verticon Dec 14 '17 at 18:24
  • Spot on :) Regarding memory, neither the buffer nor the array's actual value (a reference to the buffer) should change for just a read (including mutating a property of an element if it's a reference type). For an actual mutation, the buffer may be mutated directly if it's uniquely referenced; if it isn't, then the array will make a copy of it (by allocating a new buffer), and then mutate that (this is copy-on-write). – Hamish Dec 14 '17 at 18:48

1 Answers1

0

Classes Are Reference Types

Unlike value types(struct), reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead.

Please refer Apple documentation about Class & struct

Arun Kumar
  • 788
  • 5
  • 15