You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Iterating a Slice(T) with #each leads to iterating the slice by value. This is usually fine when T is a primitive (e.g. UInt8) or a Reference to a class instance, but when T is a struct it leads to getting a copy. Sometimes this is what we want, but at other times we'd like to avoid the copy and/or need to mutate the struct in-place.
This stems from the nature of Ruby where everything's supposed to be a Reference or a primitive, not a value that is passed by copy (except for primitives).
To solve this, we currently need to get the raw pointer and loop/increment it manually, but the intent ain't immediately explicit, in addition to be ugly Crystal:
slice.size.times do |i|
ptr = slice.to_unsafe + i
# ...end
We could at least abstract a method, for example #each_ref(&) or #each_reference(&). We'd still have to deal with a Pointer(T), but at least the iteration part would be much more readable:
structSlice(T)
defeach_ref(&)
size.times { yield to_unsafe + i }
endend
slice.each_ref do |ptr|
# ...end
The text was updated successfully, but these errors were encountered:
I found this post talking about a similar problem with Go: https://jvns.ca/blog/2024/08/06/go-structs-copied-on-assignment/. The story of that blog post is that the author previously hadn't even noticed that structs are copied on assignment and it came up in an iteration situation.
In Go you can explicitly pass structs by reference with taking a pointer (&thing, return type *Thing). Apparently dereferencing happens implicitly, so this is quite transparent for the receiver.
Anyway, the loop in the following function still caused an implicit copy, so the returned value is not a reference to the array item but a copy from the loop assignment (note: because the function returns a reference to this copy, it cannot be placed on the stack).
This is simple to resolve by changing the iteration style. Apparently taking the pointer directly from an array index accessor doesn't involve a copy and it directly returns a reference to the array item:
Iterating a Slice(T) with
#each
leads to iterating the slice by value. This is usually fine when T is a primitive (e.g. UInt8) or a Reference to a class instance, but when T is a struct it leads to getting a copy. Sometimes this is what we want, but at other times we'd like to avoid the copy and/or need to mutate the struct in-place.This stems from the nature of Ruby where everything's supposed to be a Reference or a primitive, not a value that is passed by copy (except for primitives).
To solve this, we currently need to get the raw pointer and loop/increment it manually, but the intent ain't immediately explicit, in addition to be ugly Crystal:
We could at least abstract a method, for example
#each_ref(&)
or#each_reference(&)
. We'd still have to deal with a Pointer(T), but at least the iteration part would be much more readable:The text was updated successfully, but these errors were encountered: