Start learning free

LESSON 161 OF 415 · SWIFT ESSENTIALS

Structs vs Classes in Swift: Copy or Share?

Lesson Read the full explanation

Swift gives you two ways to bundle data together: struct and class. They look almost identical on the page:

struct PlayerStruct {
    var score = 0
}
class PlayerClass {
    var score = 0
}

The difference is what happens on assignment:

  • A struct is a value type: var b = a makes a full, independent COPY. Change b, and a doesn't care.
  • A class is a reference type: let b = a copies only a reference. Both names point at ONE shared object — change it through either name and both see it.

Same syntax, opposite behavior — and it decides whether a change in one corner of your program silently shows up in another.


The trap:

Because a struct copy is a real value of its own, mutating it through a let constant is forbidden. This looks harmless:

let copy = original   // original is a struct
copy.score = 100

...and the compiler refuses it:

error: cannot assign to property: 'copy' is a 'let' constant
note: change 'let' to 'var' to make it mutable

Do what the note says: declare the copy with var. The flip side surprises people just as much: a let constant holding a class instance DOES allow copy.score = 100 — only the reference is constant, not the object it points to.


Assignment
Follow the Coding Tutorial and see both behaviors with your own eyes:

  • define PlayerStruct (struct) and PlayerClass (class), each with var score = 0,
  • make structCopy from structOriginal, set the copy's score to 100, print both,
  • make classCopy from classOriginal, set its score to 100, print both.

The exact expected output is:

struct original = 0
struct copy = 100
class original = 100
class copy = 100

Hint
Only structCopy needs var — you mutate the struct value itself. The class bindings can all stay let, because you never change what they point at, only the object's property.


STEP 1 OF 14
    Ctrl + Enter to check

    SWIFT · SWIFT ESSENTIALS

    Structs Vs Classes in Swift, explained

    Two ways to bundle data

    Swift has two keywords for defining a type that groups data: struct and class. On the page they look almost the same:

    struct PlayerStruct {
        var score = 0
    }
    class PlayerClass {
        var score = 0
    }
    

    Both give you a type with a mutable score that starts at 0, and both can be created with PlayerStruct() and PlayerClass(), because every property has a default value. The real difference shows up on assignment.

    • A struct is a value type. Assigning it to a new name makes a full, independent copy.
    • A class is a reference type. Assigning it copies only a reference, so both names point at one shared object.

    Seeing the difference

    This is the lesson's exercise. First the struct:

    let structOriginal = PlayerStruct()
    var structCopy = structOriginal
    structCopy.score = 100
    print("struct original = \(structOriginal.score)")
    print("struct copy = \(structCopy.score)")
    

    structCopy is its own value. Setting its score to 100 doesn't affect structOriginal, which still has 0. Then the class:

    let classOriginal = PlayerClass()
    let classCopy = classOriginal
    classCopy.score = 100
    print("class original = \(classOriginal.score)")
    print("class copy = \(classCopy.score)")
    

    There is only one PlayerClass object. Both constants refer to it, so the change made through classCopy is visible through classOriginal as well. The whole program prints:

    struct original = 0
    struct copy = 100
    class original = 100
    class copy = 100
    

    Step by step

    1. Declare the struct PlayerStruct and give it var score = 0. Swift infers Int from the initial value.
    2. Declare the class PlayerClass with the same property. Only the keyword differs.
    3. Create the struct original with let structOriginal = PlayerStruct(). A let is enough because it is never changed.
    4. Copy it into a var. var structCopy = structOriginal already makes the independent copy. It needs var because you change it next.
    5. Change the copy, then print both with the labels struct original and struct copy, using string interpolation: \(structOriginal.score).
    6. Repeat with the class. Create classOriginal, assign it to classCopy, set classCopy.score = 100, and print both with the labels class original and class copy. Both bindings can stay let.

    The solution in Swift

    struct PlayerStruct {
        var score = 0
    }
    class PlayerClass {
        var score = 0
    }
    let structOriginal = PlayerStruct()
    var structCopy = structOriginal
    structCopy.score = 100
    print("struct original = \(structOriginal.score)")
    print("struct copy = \(structCopy.score)")
    let classOriginal = PlayerClass()
    let classCopy = classOriginal
    classCopy.score = 100
    print("class original = \(classOriginal.score)")
    print("class copy = \(classCopy.score)")
    

    The let trap

    A let constant holding a struct is fully frozen. If you write let copy = original and then copy.score = 100, the compiler refuses:

    error: cannot assign to property: 'copy' is a 'let' constant
    note: change 'let' to 'var' to make it mutable
    

    The same applies to calling a mutating method on a struct stored in a let. The compiler reports that it cannot use a mutating member on an immutable value.

    The opposite holds for classes. A let that holds a class instance only fixes the reference: it can never be pointed at another object. The object's own var properties can still change, which is why classCopy.score = 100 compiles with let.

    Where this shows up

    • Function parameters. A struct passed to a function is copied, so the function can't change the caller's value, and a var q = p inside it changes only the local copy. A class instance passed to a function is shared, so the function can change the caller's object.
    • Identity. Two class references can be compared with === to ask whether they point at the same object. classOriginal === classCopy is true. Structs have no identity, only values.
    • Arrays are structs too. var b = a copies an array, so changing b[0] leaves a untouched.

    Choosing between them

    Use a struct for plain data where each copy should live its own life, such as coordinates or settings. Use a class when several parts of the program must share and change one object, or when you need inheritance. Sharing is powerful but surprising: a change in one place silently shows up in every other place that holds the same reference.

    Key takeaways

    • Assigning a struct copies it; assigning a class copies only the reference.
    • A copied struct must be declared with var to be changed.
    • let freezes a struct completely, but only freezes the reference to a class.
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free