LEARN SWIFT

Buy the PDF

Chapter 13 Structures

Structures are a way of grouping data together (along with methods that act on that data). In many ways they are similar to classes. One of the important differences is that structures are value types (copied when passed around your program) whereas classes are reference types (a reference rather than a copy gets passed around your program).

13.1 Structures are like classes

Classes and structures are quite similar in Swift, surprisingly so at first glance in fact. Structures are present in many languages but often they are a means to group related data together. Structures in Swift are more fully featured. In addition to storing data in structures you can also do most of the things you can do with classes with them.

Structures share the following features with classes:

  • properties (both computed and stored)
  • instance methods
  • type methods (using they static keyword instead of the class keyword)
  • initializers
  • subscripts
  • extensions
  • protocols

For the most part the syntax for each of these features is the same as it is for classes. Let’s revisit the example that we had at the start of chapter 12 on classes.

class Animal {
    var species = ""
    func description(){
        println("Species is \(species)")
    }
}

let dog = Animal()
dog.species
dog.species = "dog"
dog.species
dog.description()

This example showed how to declare a class with a property and a method.

The syntax for declaring structs is almost identical to that as for declaring classes. Except that we use the struct keyword in stead of the class keyword. In this example we can just replace class with struct and then we have a structure instead of a class.

struct Animal {
    var species = ""
    func description(){
        println("Species is \(species)")
    }
}

This example declares a structure with a property and a method similar to the class that we declared in our previous example. Declaring methods, properties, initializers, subscripts, extensions and protocols for structures follows the same syntax as for classes. Type methods are similar to classes except that they are declared using the static keyword instead of the class keyword. Since the syntax is similar and since there are so many similarities between structures and classes we won’t revisit the syntax for declaring all of these here. You can refer back to the chapter on classes for details on each of them. Instead we will focus on the differences between structures and classes.

13.2 Differences between structures and classes

We’ve seen how similar structures are to classes, not lets focus on the differences. Continuing our example above…

let dog = Animal()
dog.species
dog.species = "dog" // Error

When we try to change the value of the species property on dog we get an error: “Cannot assign to ‘species’ in dog”. This is because we declared dog as a constant using let. Previously when Animal was a class, we were able to declare dog using let but still modify the value of its species property. But now that Animal is a structure we can’t do that. Why is this? This gives us a clue as to one of the main differences between structures and classes.

Structures are value types whereas classes are reference types.

We’ll discuss value types and reference types in detail in the next section. Lets just finish this example and cover the other differences between structures and classes before we get into that.

When we declared dog as a constant it was marked immutable. Since it’s a structure (and hence a value type) the entire structure is immutable1. So we can’t change any of its properties. If we use var instead this our example will now work as it did before.

var dog = Animal()
dog.species
dog.species = "dog"
dog.species        // "dog"
dog.description()  // prints "species is dog"

We’ve outlined the main difference between structures and classes, and we’ll discuss it in greater detail in the next section. But there are a couple of other differences between classes and structures too. These are:

  • No inheritance. You can’t inherit from other structures or types.
  • No deinitializers.
  • No mutation in methods by default. By default it is assumed that methods don’t change any properties of the structure. Any attempt to change the value of a property will result in a compiler error. Functions that change the value of a property must be declared with the mutating keyword (e.g. mutating func changeDescription(newDescription: String){...}).
  • No convenience initializers, only designated initializers.

13.3 Value types and reference types

We’ve already mentioned that classes are reference types and structures are value types. Let’s look at this in a bit more detail.

Value types are always copied when they are passed around your program (e.g. as function parameters or by assignment).

With reference types, what gets passed around is a reference to the original object. So with a reference type if you assign it to 2 variables, and make a change to one of them, that change is visible to the other. With a value type, if you assign it to 2 variables each variable gets a different copy of the type, so any changes made to one are not visible to the other.

With a reference type, multiple different functions or variables can refer to something and changes made to one are visible to all the others. With a value type each function or variable gets a copy of the objects, and if they makes any changes, those changes aren’t propagated to other copies.

Let’s look at a concrete example.

 1 class AnimalClass{
 2     var description = "AnimalClass"
 3 }
 4 
 5 struct AnimalStruct{
 6     var description = "AnimalStruct"
 7 }
 8 
 9 var astruct = AnimalStruct()
10 var aclass = AnimalClass()
11 astruct.description // "AnimalStruct"
12 aclass.description  // "AnimalClass"
13 
14 func mutateDescription(c: AnimalClass, var s: AnimalStruct){
15     c.description = c.description + " mutated by mutateDescription"
16     s.description = s.description + " mutated by mutateDescription"
17     println(c.description)
18     println(s.description)
19 }
20 
21 mutateDescription(aclass, astruct)
22 // prints "AnimalClass mutated by mutateDescription"
23 // .. and "AnimalStruct mutated by mutateDescription"
24 
25 aclass.description // "AnimalClass mutated by mutateDescription"
26 astruct.description // "AnimalStruct"

In this example we declare a class and a struct and instantiate an instance of each one (lines 9-10). Both types have a description property which is just a string.

Next we define a function mutateDescription which takes an instance of our class and an instance of our struct and changes the value of the description property for each one (line 14). Note that we have to declare the structure parameter using var - if we don’t it will be immutable by default.

At line 21 we call this function, passing it aclass and astruct. What happpens here is that aclass is a reference to our instance whereas astruct gets copied and the copy is passed inside the function. When we print out the value of the struct s inside the function (line 18) we see the modified copy. But outside that function, when we print the value of astruct we see that it hasn’t been modified (line 26). In contrast aclass has been modified by the function and its value has changed after the function has been called (line 25).

We see a similar difference in behaviour between our class and structure when we assign them to variables. Continuing the previous example….

1 var astruct2 = astruct  // astruct2 is a copy of astruct
2 var aclass2 = aclass   // aclass2 is a reference to the same object as aclass
3 
4 astruct2.description // "AnimalStruct"
5 aclass2.description // "AnimalClass mutated by mutateDescription"
6 astruct2.description = "AnimalStruct2"
7 aclass2.description = "AnimalClass2"
8 astruct.description // "AnimalStruct"
9 aclass.description  // "AnimalClass2"

When we assign aclass to aclass2 here aclass2 is a reference that points to the same object as aclass. When we change the value of aclass2.description at line 5 the value of aclass.description also changes (line 9).

But when we assign astruct to astruct2, astruct2 gets a copy of astruct. The two variables refer to different things. When we change the value of astruct2.description (line 6) the value of astruct.description is unaffected (line 8).

Numbers, strings, arrays, dictionaries, tuples, structures and enumerations are all value types in Swift. Whenever you pass them around your program, they get copied.

Classes and functions are reference types in Swift. When you pass them around your program you pass a reference that can be shared.

13.4 When to use structures

It’s important to understand the benefits of structures and how to use them. Don’t just fall into the habit of modelling all your data using classes just because it lets you model it much the same way as you would have in other languages. Structures allow you to keep your data structures immutable, which makes them much easier to reason about.

Here are some things to consider when deciding whether to model something using a class or a structure.

  • Structures are for values; Classes are for references. If you need to share mutable state between different parts of your program you should use a class.
  • Structures are significantly faster, use less memory and are thread-safe.
  • Structs are easier to reason about (You don’t run into mutable state problems that are more likely with classes).
  • If you need inheritance or you know you want to pass references to an object around, use a class.
  • Objects interact with the rest of the system whereas structures appear immutable to the rest of the system. Typically a structures methods will only interact with their own data.

In “The Swift Programming Language”2, the authors outline some guidelines when choosing between structures and classes:

As a general guideline, consider creating a structure when one or more of these conditions apply:

  • The structure’s primary purpose is to encapsulate a few relatively simple data values.
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure.
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
  • The structure does not need to inherit properties or behavior from another existing type.”

In all other cases, define a class, and create instances of that class to be managed and passed by reference. In practice, this means that most custom data constructs should be classes, not structures.

So their recommendation is to default to using a class and that most custom data structures should be classes. Structures should be used in the cases listed above.

Personally, I think that your default choice should be to use a structure to model your data, unless you have a reason to model it as a class. Prefer a struct unless you know that you want a class.

In practice, the difference is mostly one of emphasis, as the cases outlined above for choosing a structure will cover most of the cases where you wouldn’t want to use a class anyway. But I think the benefits of value types are compelling enough to default to using structures over classes where appropriate.

  1. When we used a class, it was a reference to the object that became immutable rather than the object itself. So we couldn’t change the reference, but we could change the properties of the thing that the reference pointed to. 
  2. Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ie/jEUH0.l