Struct and class

Of course! That is precisely what I mean. The ability to flexibly use these paradigms in conjunction is among what makes Swift outstanding :slight_smile:

My rule of thumb in this case is

if you consider two variables "different" for reasons apart of what their properties hold, use a class. Otherwise, a struct.

Which is a different way to determine if you need an "identity" concept :blush:

4 Likes

I think I'm going to have to disagree with Apple's guidelines there. I think most "data constructs" (i.e. data structures) should behave like value types, i.e. be structs, and match their behavior by providing niceties such as COW. The only things that should end up being class types are those that must handle resources in some way, be it memory or pixels on a screen; where copying doesn't really have a good meaning. This class (ha) is smaller than you might think: a lot of the time, resources can be hidden behind handles which can then be made value types (think UnsafePointer).

2 Likes

I would consider this very bad coding style.

To add on this, different instances which have different identities of the same class may still be equal in the sense of Equality, so identity as mentioned by @DeFrenZ should not be confused with equality. These concepts are similar but still different from each other.


Fun fact:

UIColor.white == UIColor.white  // equal colors
UIColor.white === UIColor.white // equal identities

Have you used a language like C? If so, I'm sure you're familiar with what this does:

int *pointer1 = malloc(sizeof(int));
int *pointer2 = pointer1;

Here's the equivalent Swift code:

let pointer1 = UnsafeMutablePointer<Int>.allocate(capacity: 1)
let pointer2 = pointer1

Do you see now why Unsafe*Pointer is implemented as a struct?

1 Like

Yes I know C, but I would consider this pattern bad style even in C. Was it supposed to be a positiv example?

The example I've heard (and like) is that you generally should use structs if your type represents data (like maybe the cost of peanuts) and classes if your type represents objects (like your dog).

this is a catchy soundbyte but in practice i’ve found the latter case to be astonishingly rare in practice. most programs can be restructured such that almost all your things are best represented as value data. for some reason certain people expect the partition to be fifty-fifty and like, make things classes that don’t need to be just so that they’re “using enough classes”

a more useful phrase i heard somewhere is that you should define many class types but create very few class instances, while you should define few struct types, but create many struct instances. (of course, i think you should define more struct types and fewer (but not zero) class types too)

Well, it was meant to be an example. I'm not sure how you're writing C without using this style of programming, because it's done implicitly for you when you call a function that takes a pointer:

void foo(int *p) {
}

int *pointer1 = malloc(sizeof(int));
foo(pointer1) // pointer1 copied here and passed to foo

This kind of construct is so common in that it has its own Wikipedia page.

1 Like

Sure they can - By why should they? I don't get your dislike of classes. They are a useful construct for many programming tasks and structs not a silver bullet...

Come on, nobody is saying that. Classes are useful, they just shouldn’t be the default tool you reach for – that’s all.

1 Like

IIRC, core team members are unhappy with the way this is expressed and I believe there are pending radars to have this changed.

1 Like

Any visible signs to back that?

I can't find any links off-hand but I definitely remember tweets (I think) about it.

1 Like
5 Likes

Good thread. I've been confused on when to use classes versus structs. The WWDC videos I've watched appeared contradictory. Perhaps, we can iron this out -- here is my core dump. :p

If the struct is small and contains value types it will be faster. (as suggested earlier) From what I've read/studied that appears to be the case. If the struct has a reference type within it, then you might want to box that reference in a struct and avoid a allocation if the struct its a member of is copied.

Its my understanding the justification to use structs is to avoid heap allocations -- structs use stack memory. This memory is already allocated. So, the position of the top of the stack is just incremented. This is extremely fast. This is the main argument to use structs.

The heap is a constrained shared resource with potential locking. That is, if different threads are allocating, deallocting, reading etc. there is a lock around the heap. Thus, creating a class reference type is always going to be more constrained on using this resource and less performant. For instance, Games might allocate upfront just avoid this with a custom memory scheme in c/c++. So, the problem was already there in c land prior to Swift. The stack avoids this mumbo jumbo.

But ( here is the catch and confusion for me ) if the Swift struct is too big, then Swift is going to put part of it into the heap anyways -- well thats locking and slow down. ( Swift makes tables internally that reference the heap and is transparent ) I recollect it might have to do with the size of a stack frame which limits what can be stuffed into the stack? As suggested earlier, you want small structs to avoid that. So, when people say always use structs, I tend to get confused because it can't be true that its always faster.

For instance, if I have a struct with 100,000 value type Doubles in it and I change just one of those Doubles, then the entire struct will get copied (Value Type) not only on the stack but the heap too because its a "big" struct? Though, if I'm just passing the struct around, then I can use cow (copy on write) and work around if its only being read.

Also, I've heard Arrays are value types but internally they are really referenced. Like if I have a buffer pointer I'm passing to c that is an Array in Swift. So, Arrays are not really purely Value types either?

false. my post in this thread explains more

not the main, but a significant argument

okay let’s clear things up, you can totally put structs in the heap. just allocate(capacity:) space for it in memory and initialize it. swift native Arrays are also heap buffers (actually they use a weird tail-allocation thing where the array’s own metadata is stored in the same buffer) and if Element is a struct, it will be stored in the heap

you’re probably thinking of protocol type containers. completely different animal

??

i’m sure Swift is smart enough to do lvalue assigments in place, and internal structs are transparent to the compiler so i’d be shocked it it wasn’t smart enough to put the relevant pieces of structs into registers and only write to them in memory when necessary

the important word in the sentence “mutation is modeled as copyback assignment” is “modeled”.

this is correct.

you’re confusing swift’s c interop with withUnsafeMutableBufferPointer (although they do similar things)

Thanks for the clarifications in the other thread.

Ok, the main reason to use structs is avoiding reference/pointer logic and ownership issues. let and var make the language confusing when using Classes. mutating is more explicit. As you said earlier, Classes are intended for backwards compatibility with other languages. Thus, using Classes should be minimized. Classes are a first class but frowned upon.

imo, the downside is going to be the copy -- which leads to cow etc. cow seems like a hack. "Structs always pack contiguously into arrays" -- I suppose this is the stack.

Perhaps, since modern computers have many more registers, multiple cores and larger caches, the copy is not as expensive as it use to be for the vast majority of cases. So, since structs solve these other issues overall its a better choice. Optimized small will fit it into the cache better too I figure. ( just a guess ) It might have been a different situation when C was first developed which led to different design decisions on hardware with less resources. So, Swift can avoid the reference paradigm etc.

Frowned upon by the fp crowd. For the rest they are a normal element of the language.

I would very much like a post by the core team giving us the official stance on this.