Why does Apple push for structs?

Paul O'Neill
3 min readApr 26, 2022

From the very creation of the Swift language, Apple has pushed for developers to use structs frequently. Apple has an article where they state:

“The additional capabilities that classes support come at the cost of increased complexity. As a general guideline, prefer structures because they’re easier to reason about, and use classes when they’re appropriate or necessary. In practice, this means most of the custom data types you define will be structures and enumerations.”

In this article, I’d like to go over the main reasons why Apple recommends this.

Thread Safety

Thread safety is a critical topic to understand and implement throughout your code because it can cause serious bugs in your app. Probably the most common bug is data races. A data race is when at least two threads access the same data and one of those threads is performing a write operation. Data races are notoriously hard to debug because they are non-deterministic. The operating system might schedule the tasks to be done at different times.

When two threads access the same data, this is called shared mutable state. This can only happen with reference types (classes) because class instances that are accessed from two different places are pointing at the same place in memory. Structs don’t have this problem because they are value types. Value types are copied when assigned or passed to a function. This eliminates shared mutable state and therefore eliminates data races!

Sometimes shared mutable state is needed. However, Apple makes it clear in the statement above that structs are “easy to reason about”. That’s because they don’t share state. You know that changing a value of a struct will only affect the local state. That’s a huge benefit for structs.

Optimization

As mentioned before, classes are reference types and structs are values types. Structs are reasoned with locally. Behind the scenes, it means that these structs live in the Stack. The Stack is a simple “stack” data structure that holds memory for that scope of code. It’s very cheap to add things to the Stack and straightforward to handle. Classes on the other hand live in the Heap. When using a class, the Stack holds a reference to the data and the data itself is stored in the Heap. When storing something in the Heap, a place in memory has to be found that fits the right size of the data to be stored and then stores it.

When we’re dealing with reference types we also have to worry about reference counts. While Swift handles the process of retaining and releasing data, if we structure our code in a certain way, we could cause the data to never be released because we haven’t properly handled the references. This is referred to as retain cycles. We don’t have to worry about retain cycles with value types, because there are no references to be counted!

Boilerplate code

When we create a class and don’t create default values for our attributes, the compiler will force you to create an initializer and set all the attributes that need to be set. With structs, we don’t need to do that. Structs come with memberwise initializers that automatically create an initializer with all of the attributes that need to be set. This isn’t a game-changing feature of structs, but it’s nice to remove boilerplate code that would be required with classes.

In Summary

Apple recommends developers to use structs when creating types as a default because in a lot of cases structs will handle your needs just fine. You will also avoid an array of bugs because of the reduction of complexity that structs offer. If you’re having a hard time deciding whether a class is needed in a certain case or not, I’d recommend a previous article of mine that goes over the question of how to decide between a class and a struct.

If you have suggestions or find errors in my logic, please comment below. I’d love to hear and learn from you.

--

--