Structs are similar to classes in the way you define them. If you look at the Pet class you defined earlier, it might be easy to miss the fact that it's a struct. If you pay close attention, you will notice one big difference though. You didn't have to write an initializer for the struct! Swift can automatically generate initializers for structs. This is extremely convenient and can save you a lot of typing for larger structs.
Structs also can't inherit functionality from other objects. This means that structs always have a very flat and transparent set of properties and methods. This allows the compiler to make optimizations to your code that make structs extremely lightweight and fast.
A struct can, however, conform to protocols. The Swift standard library is full of protocols that define features for many of the built-in types, such as Array, Dictionary, and Collection. Most of these built-in types are implemented as structs that adopt one or more protocols.
One last thing you need to understand about structs is that they are very strict about whether they can be modified. Consider a struct that looks as follows:
struct Car { var gasRemaining: Double func fillGasTank() { gasRemaining = 1 } }
This struct will cause the compiler to throw an error. A struct itself is immutable by default, which means you cannot change any of its values. It's up to you to make it explicit to the compiler when a method can mutate, or change, a struct. You do this by adding the mutating keyword to a function, as follows:
mutating func fillGasTank() { gasRemaining = 1 }
When you create a constant instance of Car and call fillGasTank() on it, the compiler will error again. If you call a mutating function on a let instance, you mutate the instance, meaning the value of the property would change. Because of this, you can only call mutating functions on variable properties.