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.