I think it’s helpful to think it like this:
- classes are reference types: they are a pointer to the actual data
- structs are value types: their data is inlined
What that means is when you do:
x = SomeClass.new
x will be a pointer pointing to the data that SomeClass holds. Something like:
x = pointer -> [...data (instance vars)...]
and [...data...] is allocated on the heap.
If you have a type that has an instance variable of type SomeClass, that instance variable is also a pointer to the data. When you assign x to that instance variable that pointer is copied.
Note that when you do x = SomeClass.new, the pointer for x is stored on the stack.
With a struct, the data is inlined:
x = SomeStruct.new
so essentially x is the data:
x = [...data...]
and x is allocated on the stack.
If you have a type with an instance variable of type SomeStruct, that instance variable will not be a pointer to the data but it will have all the space for the struct. When you assign x to that instance variable, the data will be copied to that space.
So let’s say SomeClass has an instance variable s of type SomeStruct, and SomeStruct has two instance variables of type Int32. You do:
c = SomeClass.new
c.s = SomeStruct.new
it will be:
c = pointer -> [s = [x: Int32, y : Int32]]
note that everything after -> is in heap memory, which in this case will be 8 bytes.
When you do c.s = SomeStruct.new, that entire data is moved (copied) into the heap.
If it’s the other way around, and SomeStruct has an instance variable of type SomeClass, which in turn has two integers:
s = SomeStruct.new
s.c = SomeClass.new
it will be like this:
s = [c = pointer -> [x: Int32, y : Int32]]
Note that s in this case has its data inlined, and because it only holds a SomeClass, which is represented as a pointer, s's data is just a pointer. The data for that pointer always lives on the heap.
When you do:
c = SomeClass.new
s.c = c
the pointer for c, which lives in the stack (but the data lives in the heap) is copied from the stack to the struct (which in this case also lives in the stack).
I hope this is clear!