Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Should I use a Swift struct or a class? (sealedabstract.com)
53 points by ingve on July 4, 2015 | hide | past | favorite | 52 comments


There's a ton of discussion in the Swift world about when to use structs and when to use classes. And I just don't understand why.

Use structs when you want value semantics. Use classes when you want reference semantics. Boom. Done.

All the confusion and discussion and fighting seems to be because people don't understand the implications of value versus reference semantics, and instead of learning they try to come up with ad hoc rules and guidelines that will let them decide without understanding the actual differences.


"And I just don't understand why."

I think you've correctly identified the source of confusion -- value vs reference. To you, that might be plain as day. But, to many iOS devs, they really couldn't explain the nuances.

I mean, look how much trouble beginner programmers have with the concept of Pointers for pete's sake.

But, I think you're right -- weird rules and guidelines just confuse the issue.

Andy Matuschak's functional swift talk puts the ref vs value discussion in practical terms of a Khan Academy drawing app:

http://2014.funswiftconf.com/speakers/andy.html


The other issue is that too many programmers these days don't have a solid computer science background... I'm all for people being self-taught and learning programming on their own... But the downside of that prevents individuals from learning how things work at a lower level.

I've never written a line of Obj-C or Swift in my life, but all you have to tell me is "struct = value based, class = referenced based" and I immediately know how it works...


I'm going to take issue with this. This has nothing to do with having a solid computer science background -- in fact, I've seen way more architecture astronauts with CS degrees.

It has to do with learning stuff on a lower level -- whether or not you attend a school to do so. Us autodidacts who grew up on assembly language and C code will beg to differ on your use of the word "prevents".


I presume he's not talking about long-time autodidacts. He's talking about the many programmers who have six months of JavaScript experience from a bootcamp and are learning Swift.


I guess I'll ask this here, since you seem to know what you're talking about. I'm a cs major, and I just finished my first c class. I quite enjoyed getting my feet wet with the lower level aspects of c, c doesn't hold your hand much (compared to java or python at least) and I liked that you really have to think about what you want, and the best way to get it with c.

Do you have any books or exercises to recommend to help further my understanding of low-level computing? I'll be taking too and compilers in the fall, but I'd like to keep digging on my own.


The issue is that most of the cocoa framework requires classes to work with it. For anything UI, you are going to be dealing with reference semantics unless apple gives us an alternative to cocoa touch like http://componentkit.org


I don't doubt that a lot of people fail to understand value versus reference semantics. What I find weird is how, rather than learning it, people keep trying to come up with crazy rules to avoid learning it. I guess they don't realize that it's the key here.


Agreed. You see the same vague cargo-culting in C#. The popularity of both (as the de-facto standards on Windows and OS X/iOS) has just led to a lot of people who've learned enough to "get things done" rather than understanding both practical and theoretical underpinnings of the systems that they're using.

It worries me, to a degree, because there's probably no way to make a programming language smart enough to protect users from these people.


In C#, there's a lot more issues though with val vs ref. The CLR JIT had some serious issues with structs. OTOH, there still is no way to stackalloc reference types, so in many cases you're forced to use structs so you can get decent performance. But I've zero formal training, and I've never found val vs ref particularly confusing. Though I did wonder why VB needed ByVal at all; couldn't it all be done ByRef?

But wow, this post... I've no idea how he came to the conclusion structs=functional. He just sorta dives into it? If you aren't sure on something, why not read up a bit more? Guess C is a really functional language since it's got structs everywhere.


There is no way to allocate objects of reference types on stack because references can easily outlive objects, leaving you with dangling references. And as I understand ByVal in VB.NET is redundant since it's the default option. It's just a leftover from VB6 where the default was ByRef. Crazy language.


> There is no way to allocate objects of reference types on stack because references can easily outlive objects,

That's simply due to a lack of analysis. In many cases it's easy to show the object is short lived. Yet there's no way to annotate this and AFAIK, the JIT doesn't even try to figure this out.


I think the problem is that most people coming to Swift come from languages like Ruby and JS where just about everything is a reference (or pass by value where every value is a reference anyway). The distinction between value and reference is really hard to grasp when your background is in languages that are opaque like that. I remember Go had tons of questions of this nature early on in its adoption upswing.


The thing is, every language I'm familiar with has some types with value semantics. Numbers, for example, are pretty much always values. Pretty much nobody is surprised at the behavior of code like this in languages like Ruby and JS:

    var x = 42
    var y = x
    x++
    print x // 43
    print y // 42
It seems that, for whatever reason, people understand this but have a really hard time generalizing it. It's not even a matter of custom types being a confounding factor, as I've seen deep confusion over the fact that Swift collections have value semantics.


That's odd. You'd think most people would be coming from objc


Just the same in Objective C, unless you're dealing with a C struct (not being passed by pointer) or a primitive type you're in a sea of Objective C references. Value types are brand new to the non-Foundation framework ecosystem as far as writing new, stylistically correct, code is concerned. This in my experience does seem to be a sticking point for people without a good bit of experience in languages with the same or similar value and reference type split (C++, Java, C#, Rust, etc).


Warning: I haven't learned Swift yet. So let's say you want to use value semantics so you choose a struct. But later you find that you have to change state of the values. Is your only resort then to switch to using classes? Because if that's true that's a false choice.


Values can still be mutated. Value versus reference isn't about mutable versus immutable. It's about whether when you do a = b you get a new instance or just a new reference to the same instance.


Ok thanks. Looking at this from a Lisp programmer's perspective values are by definition immutable. The Swift ecosystem seems very complex.


Variables can be mutated in Lisp. That's all this is.


Variables are not values.


Thr point is, this is a matter of definition, in that people often use the word "value" where "variable" or "object" could be used, especially as to distinguish from references. This is common usage in other languages too. It has nothing to do with Swift being complex.


Values can't be mutated if they're Ivars and you're in a non-mutating func. Non-mutating funcs are unique to value semantics.


True but I'm not sure what your point is. If you need a mutating struct method then of course you have to mark it as mutating.


One does not "simply" mark a method as mutating.

A) One must ensure that one's callers are also mutating, or otherwise have an assignable reference, all the way up the stack

B) One must mark the applicable function in the protocol interface as mutating, which may or may not be under your control

C) One must now repeat step A for all consumers of the protocol in step B, even if none of the other implementations of that protocol mutate their own state

Of course one can design one's architecture specifically with a view toward making ABC trivial, but in the general, unrestricted, default, I-had-other-problems-that-day-and-didn't-think-about-it case, they are hard.

That is why I find calls to "just learn value semantics" necessary but insufficient. Value semantics will not refactor my code when I discover-by-surprise that 18 functions need to become mutating, breaking API compatibility across several build targets, because I failed to forsee a necessary mutation 8 months ago.


I have trouble imagining a scenario like you describe. "All the way up the stack" should be, what, two or three levels at most? And whether or not a particular method is one that changes state or just fetches it should be evident from its semantics. What are you doing where you would suddenly discover that a method is mutating, months after you originally created that method?


The point is that you don't always want to mark the method as mutating. This is explained in the article.


This is described in the article. I wouldn't say that it's explained. It makes no sense to me. Whether a method mutates state should be evident when you make it. The very concept of a value-type method which mutates state but isn't marked as mutating makes no sense at all. It sounds like it might be a case where you actually wanted reference semantics but accidentally used value semantics and are trying to make it work anyway.


OK, I can appreciate your stance, but can you explain the difference? (Or at least point to a reference that does?)


  let a = MyStruct(x: 1, y: 2)
  let b = a // Creates a copy of `a`
  b.x = 3 // Won't compile because you used "let", so its properties are immutable
  var c = a
  c.x = 3
  println(a.x) // Prints "1"; the original instance doesn't get changed

  let d = MyClass(x: 1, y: 2)
  let e = d // Refers to the same object as `d`
  e.x = 3 // Allowed, even though you used "let"
  println(d.x) // Prints "3"
That's a basic illustration of the difference between value types and reference types, but there are other differences between structs and classes as well (for instance, structs don't support inheritance or deinitializers).


Nice example, thanks!


It comes down to equality. Values have "extensional equality" which means that values A and B are equal exactly and only when they look the same and can be used in the same way. On the other hand, most people are used to "referential equality" which is more strict. It lets me distinguish "your copy" of A from "my copy" of A via their names.

Without referential equality things like mutation fail to have any sense, but programs are in general simpler.


Cool, thanks for the distinction! Gives me some terms to look up for more info, too.


Functional programming isn't about eliminating state. You can't write programs without state. Functional programming is about eliminating hidden state changes.

State changes are fine! They just aren't allowed to be hidden.

Despite all that - I agree that move semantics are weird. He's got a point there.


In the presence of concurrency, shared mutable state is a disaster. There are two ways to fix this:

1) Make it not mutable 2) Make it not shared

But these are still "state", so I'm not really sure what the hell the article is on about.


3) synchronize correctly, which is fraught in nearly all languages. Rust has some very nice facilities for this, however, that make shared mutable state very usable indeed.


If you don't have shared mutable state or shared resources, then you don't really have concurrency. Concurrency is actually defined as this problem, as opposed to parallelism which is just about splitting up work.


One thing I've learned, if you want to convince people you're right, best to insult them and tell them they're "on crack".

Guaranteed to work.


This post is basically saying "I don't understand programming and I won't even try"


Surprisingly salient and cogent points for an article that's so hostile and willfully ignorant.

The author is correct about when to use both, and I'm glad he doesn't discount structs entirely like I've seen from some OOP strongholds.

"The way they write functional programs for decidedly non-functional problems is through a trick called a monad, which I will not explain and nobody understands anyway,"

Maybe you'd be less hostile if you actually took the time to learn about generic abstractions and how they can greatly simplify a codebase.

Hint: Optional is a monad, and you can use it as such.


Not sure though why you justify the fact that you should'nt use singletons embedded in a struct because they are copied by reference? If it is a singleton, you want it to be copied by reference. And you actually expect it to be since you want all the structs to refer to the same object?

You should not use an embedded class in a struct if it is not a singleton, as in the UIBezierPath example, where when you copy the struct, you expect to do a copy-on-write of the class to avoid mutating all copies in all structs


Seems like a pretty good article, but as a blog, his site is violating the "put a date" principle: http://www.observationalhazard.com/2014/09/put-date-on-your-...


If your understanding of functional programming regarding state management is monads, I'd argue you don't understand. Monads are the guided nuclear missile of functional state management. If you don't know why you need them you don't need them. There are simpler ways to manage state in FP.


Monads aren't nouns. When you build a state transformer you can choose whether or not to recognize that it is a monad (or maybe strong profunctor, depends upon your design) but it always just IS one.


or: how I stopped worrying and accepted making programs that are hard to reason about


If you want to contribute, write why you disagree instead of attacking the author as a person.


Wasn't trying to attack; sometimes trying to create a perfect mathematical model for your program can get in the way of having a program at all.


Right? I've always been amazed at how people just accept this state of affairs.


Also why no mention for the enum, the other value type that allows for pattern matching?


Ah, an excuse why two almost identical language constructs exist. Welcome to the list of cluttered programing languages.


So ios devs, who supposedly need to write in a memory unsafe language because that's just better, don't understand the basic difference between pass by reference and pass by value.

Nice. What can possibly go wrong?

Looking forward to seeing the questions in the swift SO site.


What a stupid, long blog post




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: