Structs and protocols
A struct that models a thing, properties it computes on demand, and a protocol that states what a type can do.
A struct is a small model
A struct gathers related values under a name. struct Rectangle with a width and a height describes a rectangle, and Swift writes a ready-made initialiser so you can build one with Rectangle(width: 3, height: 4).
Stored properties are the values the struct remembers. Methods are functions that live inside the struct and work on those values.
The member-wise initialiser is generated only when the struct declares no initialiser of its own. Write your own init and the free one disappears, so a struct with validation usually needs an initialiser that checks each value before storing it. A property with a default can still be left out of the call, and a private property makes the generated initialiser private too.
Properties you compute
A computed property looks like a stored one at the point of use, but it works the value out fresh each time you ask. An area read as box.area has no parentheses, yet it always reflects the current width and height.
Because it is computed, it can never drift out of step with the values it is built from. Change the width, and the next read of area uses the new number.
A computed property has no storage of its own. It runs its block on every read, so box.area twice is two multiplications, not one saved answer. That is cheap here and important when the computation is expensive: cache the result in a stored property if it is read far more often than the values change.
A protocol is a promise
A protocol lists what a type can do without saying how. protocol Named might only require a label property that can be read. Any type that declares it conforms must supply that property.
A function can then accept any value that conforms, using the protocol name as the parameter type, and call the promised members without caring what the underlying type is. That is how one piece of code serves many kinds of thing.
Conformance is declared on the type, not on the protocol: struct Point: Named is the promise. The compiler checks the member’s name, type and access level. A protocol can also require a method with arguments, and the conforming type must match the parameter labels exactly, because the protocol is describing the call site as well as the body.
Structs are copied, not shared
A struct is a value type. Assigning it to a new name copies the values, and changing the copy leaves the original alone. That makes a struct predictable to pass around, because no other part of the program can change it behind your back.
A method that changes a stored property must be marked mutating, and it can only be called on a var instance. The compiler asks for that word so a reader can see, at the definition, which methods write.
Passing a struct to a function copies it, so the function cannot change the caller’s value by default. An inout parameter is the explicit exception: the caller writes &value, and the changes are written back on return.
What goes wrong
A missing protocol member is reported on the type declaration, not at the call that needs it. The message names the requirement and the type, so read it as a checklist: the property is not readable, the method has the wrong label, or conformance was never declared.
A mutating method on a let instance fails with a message about an immutable value. The fix is not to mark the method non-mutating; it is to hold the instance in a var if it really changes. If it does not change, remove the assignment and the keyword together, because a method that claims to mutate but does not is misleading.
A struct that keeps a promise
protocol Named {
var label: String { get }
}
struct Point: Named {
var x: Int
var y: Int
var label: String {
return "(\(x), \(y))"
}
}
let p = Point(x: 3, y: 4)
print(p.label)Three mistakes follow from value semantics. A let instance of a struct cannot be mutated, so the variable that holds it must be a var. A method that assigns to a property needs the mutating keyword; without it the compiler treats the assignment as a change to a constant. And a type that declares conformance must supply every member the protocol names, with the exact type, or the compiler rejects the type itself.
A rectangle that knows its area
Run it and check the area and the label. Then change the width and predict how the area and the label each change.
Try these next
- Add a
mutatingmethod to a struct, call it twice, and print the property after each call. - Copy a struct into a
var, change the copy, and print both values to show they are independent. - Write a second type that conforms to the same protocol and pass both types to one function.
A counter, a copy and a promise
Run it and compare the original with the copy. Then increment the copy twice more and predict both summaries before you run it again.
A protocol is a contract, not a parent. It does not share code, it shares a promise, so a function written against the protocol keeps working when a new conforming type appears.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
Where does Rectangle(width: 3, height: 4) come from?
