Let | Var | Reference & Value Types

This is about as short a post as it can get! For a deeper look at reference & value types in Swift, see my other post on the topic.

I see a lot of questions on SO that discuss mutability in Swift. Let's go over some basic points about mutable (and immutable) objects in Swift:

  1. Creating a let variable enforces immutability - that variable cannot point to a different piece of data. You cannot change the data at that memory adddress and you also can't point the variable to a different memory address. Say you have let a = 5 and let b = 8.
    You cannot then say a = b nor can you say a = 7.
  2. If you have a var variable, all that means is that your variable can be reassigned - that could mean that the value it points to changes or that you point it to a different memory address.
  3. Structs, enums and other Swift value types (primitives such as Int, Double, String, Array, Dictionary and Set) dispay deep copy semantics. This means that if two variable are pointing to the same memory location, changing the data of one won't change the data of the other. A deep copy of the changed variable will be made and it'll point to its own separate memory address. Each value type instance holds a unique reference to its data. The ability of values to point to the same memory address until they no longer have the same data is due to Swift's Copy-On-Write optimization.
    //Assume Student() is a struct that initializes a student with an age.
    let a = Student(25)
    let b = a
    print(a.age) //25
    print(b.age) //25
    a.age = 35
    print(a.age) //35
    print(b.age) //25
    • As you can see above, changing a property value for a won't cause a change in b. Additionally, both will point to the same memory address until we change a.age - then a deep copy is made, passed to b, and the two variables point to different places in memory
  4. With reference types (often classes) multiple instances can share a single copy of data (they can point to the same memory address). If we pass a class instance to a function, for example, we're actually passing in a reference to the memory address of the object.
    //Assume Student() is a class that initializes a student with an age.
    let a = Student(25)
    let b = a
    print(a.age) //25
    print(b.age) //25
    a.age = 35
    print(a.age) //35
    print(b.age) //35
    • Since we're dealing with reference types, a shallow copy is made, meaning both instances are pointing to the same memory address. A change to data in one will result in change to data in the other.
    • With reference types, it's easy to leave around many reference to an object. Changing data of one reference will change the rest, and this can result in unexpected behavior and leads to bugs. Immutable types are considered safer and can lead to more bug-free code.