DEV Community

Swift Classes — Everything You Knew About Structs Just Got More Complicated

Gamya on July 18, 2026

Okay so. We need to talk about classes. If you've been following along with this series, you're probably feeling pretty comfortable with structs a...
Collapse
 
vinimabreu profile image
Vinicius Pereira

"Structs give every copy its own data. Classes make all copies share the same data" is the right mental model, and there is a nuance worth adding so nobody fears the copies: for the standard collections (Array, Dictionary, String), Swift implements this with copy-on-write. Assigning a struct-backed array to a new variable copies a reference under the hood, and the real copy only happens at the moment one of them mutates. So the semantics are "every copy is independent" while the physics are lazy, which is why passing big value types around is cheaper than the mental model suggests.

The let surprise with classes also generalizes into a rule that transfers to every language: let freezes the binding, not the object. A let class instance means "this variable will always point at that object," not "that object cannot change." It is the same distinction as const in JavaScript or final in Java. Value types are the special case where freezing the binding effectively freezes the data too, and once you see it that way, the behavior stops being surprising and becomes a definition.

Collapse
 
gamya_m profile image
Gamya

The copy-on-write point is such a useful addition — because the mental model of "every copy is independent" is the right one to hold onto for reasoning about correctness, but "the physics are lazy" is what makes the performance story not actually scary. Those two things are true simultaneously and it's worth knowing both.

The "let freezes the binding, not the object" generalization is the cleaner version of the signpost analogy honestly — and you're right that it transfers directly. Once you've seen it that way, the Swift behavior stops being a quirk to memorize and becomes a specific instance of a broader rule that shows up in almost every language. The value types being the special case where binding and data happen to be the same thing is the inversion that makes the whole thing click. Really appreciate you adding this layer — it's exactly the kind of context that turns "I know how this works" into "I understand why this works." 🌸

Collapse
 
vinimabreu profile image
Vinicius Pereira

Appreciate that. One last spot where the binding rule pays off: it explains the mutating keyword. A mutating method reassigns self, the whole value, which is exactly why the compiler rejects it on a let struct. Same freeze-the-binding rule, just surfacing in the method system instead of the variable. Once that clicks, "cannot use mutating member on immutable value" stops being a cryptic error and becomes the rule restating itself.

Thread Thread
 
gamya_m profile image
Gamya

That's the connection I hadn't made explicit — mutating reassigning self under the hood is exactly why the freeze-the-binding rule shows up there too. It's not a separate rule about methods, it's the same rule expressing itself in a different context. Once you see it that way, "cannot use mutating member on immutable value" stops being a thing to memorize and becomes something you could have predicted from first principles. That's the kind of unification that makes a mental model actually useful rather than just a collection of facts. 🌸

Thread Thread
 
vinimabreu profile image
Vinicius Pereira

"Predicted from first principles rather than memorized" is the whole reason it's worth chasing the single rule instead of collecting the special cases. That's the difference between knowing a language and being able to reason in it. Genuinely good thread, thanks for taking it this far with me.

Thread Thread
 
gamya_m profile image
Gamya

"Knowing a language vs being able to reason in it" is exactly the distinction worth chasing, and it's what makes these threads more valuable than the articles alone honestly. The article gives you the map, the conversation finds out whether the map holds up under pressure. Really glad you pushed it this far, genuinely one of the better exchanges I've had on here.

Collapse
 
technogamerz profile image
𝐓𝐡𝐞 𝐋𝐚𝐳𝐲 𝐆𝐢𝐫𝐥 • Edited

Thanks for taking the time to write this article! ❤️ Swift's transition from structs to classes is one of those topics that seems simple at first, but you've highlighted why it becomes much more nuanced once reference semantics, identity, and shared mutable state enter the picture.

What stood out to me is that this isn't just about learning a new keyword—it's about changing the way a developer thinks. Structs encourage predictable, value-oriented design, while classes introduce identity, lifecycle, and the responsibility of managing shared state. That's a mental shift many beginners underestimate, and your article helps bridge that gap.

One thought that could make this even stronger is adding a section on decision-making rather than just differences. For example: "If you're building a model that represents immutable data, choose a struct. If multiple objects need to observe and mutate the same instance, a class may be the better fit." Real-world scenarios like networking models, SwiftUI state, or caching objects would help readers develop intuition instead of memorizing rules.

It could also be interesting to briefly connect this discussion with Swift's emphasis on protocol-oriented programming. Many experienced Swift developers start with structs by default and only reach for classes when identity or inheritance is genuinely required. Understanding why that philosophy exists is often more valuable than simply knowing the syntax.

Overall, this was a thoughtful read. Thanks again for sharing it and encouraging developers to think beyond the surface-level differences. Articles like this help people build stronger design instincts, not just better Swift syntax. 👏🏻🙂

Collapse
 
gamya_m profile image
Gamya

Thank you for this—and those are genuinely good suggestions! 😊 The decision-making framework point especially—you're right that knowing the differences is step one, but knowing when to reach for each one is where the real instinct gets built. I deliberately kept this one focused on the "what's different" part because I didn't want to overload a single article, but the "how to choose" angle is a great candidate for a follow-up, maybe once protocols are covered since that's where the "structs by default" philosophy really starts to make sense.

The protocol-oriented programming point is one I'm looking forward to getting to in the series—it reframes a lot of the structs vs. classes conversation in a way that makes the Swift philosophy click rather than just feeling like a set of rules to memorize. Really appreciate the thoughtful read! 🌸

Collapse
 
the17yodev profile image
Young Dev

Nice article! I like how you focused on the practical differences instead of only explaining the theory. The examples were clear and easy to follow. 👏

Collapse
 
gamya_m profile image
Gamya

Thank you! The theory only really lands when you can see it doing something concrete—glad the examples helped! 🌸

Collapse
 
umitomo-lab profile image
Umitomo

Thank you for sharing such a great article😄 . It was really helpful for understanding Swift classes more clearly.

Collapse
 
gamya_m profile image
Gamya

Thank you for reading! Really glad it helped clarify things 😊🌸