Celebrating 10 Years!

profile picture

The difference between classes and structs in Swift

February 23, 2016 - Roundwall Software

Today I assigned my students (I teach a bootcamp now) to write 400-500 words about one of the topics we have covered in class, so I thought I would do the same. Here goes!

Classes and structs are similar. Both can have properties, both can have methods. This can make it a bit confusing for developers who are new to Swift. Understanding the difference between the two can help to make choosing between the two easier in your code.

Inheritance

Classes give you the ability to make use of inheritance, much like you would expect from other languages. While most developers will discourage you from using inheritance to solve your problems, they're still useful sometimes. Structs cannot have "substructs" or whatever you might call them. They are effectively final.

class Subclass: SomeOtherClass {
  // This is just fine
}

struct SubStruct: SomeOtherStruct {
  // This will get you compiler errors, not possible!
}

# References vs values

This one is a bit tricky and was the cause of most of my students having confusion moments in the first week of our bootcamp. When you pass around instances of a class, you are actually just passing around a pointer to that class. When you pass around structs, you are passing the actual value of the stuct (a copy even).

class Town {
  var population = 10
}

class Zombie {
  var town: Town
  func terrorize() {
    town.population -= 2
  }
}

class Vampire {
  var town: Town
  func terrorize() {
    town.population -= 1
  }
}

Let's say you have 3 classes, a town, a zombie, and a vampire. The zombie and vampire expect a town that they can terrorize. If we create one town and assign the same town to both monsters, they will be sharing a reference to the same instance of that town.

let town = Town()

let zombie = Zombie(town: town)
let vampire = Vampire(town: town)

If both monsters terrorize that town, your town will have a population of 7 in the end, because both monsters terrorize the same town.

zombie.terrorize()
vampire.terrorize()
print(town.population) // <-- This will print a 7

This is what we'd expect. Our poor town suffers from two different monsters killing their people. Poor people.

With a struct this happens differently. If we implemented the same objects as structs instead of instances of classes, we would get an entirely different answer.

struct Town {
  var population = 10
}

struct Zombie {
  var town: Town
  mutating func terrorize() {
    town.population -= 2
  }
}

struct Vampire {
  var town: Town
  mutating func terrorize() {
    town.population -=  1

Now if we make a zombie and a vampire and give it our town we can see a big difference:

var town = Town()
var zombie = Zombie(town: town)
var vampire = Vampire(town: town)

zombie.terrorize()
vampire.terrorize()
print(town.population) // What?!? It says 10 even though our monsters attacked.

So first you might look at this and think, "oh hey, I found a bug in Swift" or even, "hey maybe I wrote this incorrectly". Neither one is true! Your code is totally valid, but structs are passed around by value. The zombie and vampire both have a town, but it's not the same town that our original town variable is holding onto. They're working with an entirely different object that just happens to have the exact same values as our original town variable had. To use structs to model our monster attacks, we'd need to do things differently.

So now you know two of the big ways that structs are different than classes. And knowing is half the battle.