Ok, this topic name is fishy - it’s not about questions but about answers))
I’ve made a simple wrapper that holds a pointer to struct inside and forwards all setters and getters to it. This way you can do
some_struct.header.id += 1
or
increment_position(my_struct)
without a fear that a copy will be modified.
You can check it here: GitHub - konovod/ref_record
There is still one big drawback - a need to manually mark fields that contain struct to make it possible to correctly nest them. Hopefully it is possible to solve it with some clever trick.
1 Like
What’s the point, though?
Reference semantics are much better expressed in a class. That’s much cleaner and has fewer quirks than mutating structs.
If the goal is to avoid allocations, you can put the class instance in non-heap memory, for example with Reference.unsafe_construct (experimental, discussion in #13481).
1 Like
Classes has overhead (one pointer per instance, i think?), so they are bad in two cases:
- array of small structs (where even one pointer will significantly increase size)
- when struct layout is defined elsewhere (lowlevel protocols, interfacing with external libs, also mcu SDK)
So while Reference.unsafe_construct is awesome and this wrapper is solving similar problem from another end, I don’t think Reference would replace all uses of mutable structs.
Oh, nice. A zero runtime cost abstraction, and we don’t have to deal with the Pointer. Still dangerous as we can easily pass it around and forget it might point to unsafe memory 
I’ll note that we can abuse leverage the method_missing macro to wrap any struct. See this Carcin PoC 
2 Likes
The method_missing approach is elegant, but the problem is that it doesn’t solve situations when structs are nested:
line = Line.new(Point.new(1, 2), Point.new(3, 4))
ref = Ref.new(pointerof(line))
p ref.start.x
ref.start.x = 0
p ref.start.x
Yeah, you must explicitly Ref(Point) too, which ref record does for you.