One of the major differences between structs and classes is how they handle constants.
When an instance of a struct is declared as a constant (using let
), all its properties become constant, even if they are declared as variables within the struct. To modify the properties of a struct, you need to mark methods with the mutating
keyword.
In contrast, when an instance of a class is declared as a constant, you can still modify its properties as long as they are declared as variables. Therefore, classes do not require the mutating
keyword for methods that change properties; this keyword is only necessary for structs.
Let’s understand mutability in Structs and Classes in Swift :
Mutability in Structs:
Here’s a struct called Student
with a property name
, and a method to update the name. We will also see how declaring an instance of the struct as a constant affects its mutability.
struct Student { var name = "Sid" // Mutating method to modify properties mutating func updateName(name: String) { self.name = name } } // Declaring an instance of Student as a constant let student_const = Student() // Attempting to modify properties will result in a compile-time error // student_const.name = "Shivi" //Error: Cannot assign to property: 'student' is a 'let' constant // Attempting to call a mutating method will also result in a compile-time error // student_const.updateName(name: "Shivi") //Error: Cannot use mutating member on immutable value: 'student' is a 'let' constant // Declaring an instance of Student as a variable var student_var = Student() // Modifying properties directly student_var.name = "Shivi" print(student_var) //Output: Student(name: "Shivi") // Using the mutating method to modify properties student_var.updateName(name: "Shivi") print(student_var) //output: Student(name: "Shivi")
Explanation
Declaring as a Constant:
let student_const = Student()
When we declare Student
using let
, it is a constant. This means that its property name
is also considered constant, even though it is declared as variables within the struct.
Attempting to modify student_const.name
directly will result in a compile-time error. Similarly, attempting to call a mutating method on Student
will also result in a compile-time error.
Declaring as a Variable:
var student_var = Student()
When we declare Student
using var
, it is a variable. This allows us to modify its properties directly or by using a mutating method.
The updateName(name: String)
method is marked as mutating
, which allows it to modify the property name of the struct instance. We can call this method on Student
to change its properties.
This example demonstrates how declaring an instance of a struct as a constant (let
) or a variable (var
) affects its mutability and how mutating methods work with struct instances.
Mutability in Classes:
In Swift, classes exhibit reference semantics, meaning that when you modify an instance of a class, all references to that instance see the change. The mutability of a class instance depends on how its properties are declared and how the instance itself is referenced.
Key Points on Mutability in Classes:
Instance Properties:
- Properties declared with
var
can be modified. - Properties declared with
let
cannot be modified.
Instance References:
- A class instance declared with
var
allows modification of its properties. - A class instance declared with
let
still allows modification of its properties, provided those properties are declared withvar
. However, you cannot reassign the instance to a different object.
Example
Let’s illustrate mutability with a class named Person
.
class Person { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age } func haveBirthday() { age += 1 } } // Creating a class instance let person1 = Person(name: "Alice", age: 30) // Modifying properties of a class instance declared with `let` person1.name = "Alicia" person1.haveBirthday() print(person1.name) // Output: Alicia print(person1.age) // Output: 31 // Attempting to reassign a class instance declared with `let` (will cause an error) // person1 = Person(name: "Bob", age: 25) // Error: Cannot assign to value: 'person1' is a 'let' constant // Creating a class instance declared with `var` var person2 = Person(name: "Bob", age: 40) // Modifying properties of a class instance declared with `var` person2.name = "Robert" person2.haveBirthday() print(person2.name) // Output: Robert print(person2.age) // Output: 41 // Reassigning a class instance declared with `var` person2 = Person(name: "Charlie", age: 25) print(person2.name) // Output: Charlie print(person2.age) // Output: 25
Explanation:
Modifying Properties with let
Reference:
-
person1
is declared withlet
. This means we cannot reassignperson1
to a newPerson
instance, but we can still modify its properties (name
andage
) because they are declared withvar
.
Modifying Properties with var
Reference:
-
person2
is declared withvar
. This allows us to modify its properties as well as reassignperson2
to a differentPerson
instance.
Methods and Mutability:
-
- The
haveBirthday
method increments theage
property. This method can modify the state of thePerson
instance without requiring any special keywords likemutating
(which is required for structs).
- The
By understanding these aspects, you can effectively manage mutability and reference semantics in your Swift programs when using classes.
FAQs
Q. Why Struct’s properties cannot be modified within an instance method ?
Ans. Structs are value types, meaning that every time a struct is assigned to a new variable or passed to a function, a copy of the original instance is made. This behavior inherently suggests immutability because changes to one instance do not affect any other instance.
When you mutate a struct’s property, it’s semantically equivalent to creating a new instance of the struct with the modified property. This is because of the value semantics — any change creates a new value rather than altering the existing one.
Because structs are intended to be immutable by default, the Swift compiler enforces this immutability unless you explicitly indicate that a method will change the struct’s state. The mutating
keyword serves this purpose. It tells the compiler and other developers that the method will modify the instance’s properties or even reassign self
to a new instance.
Read More: