Here is an example:
require "spec"
struct S
property x : Int32
def initialize(@x)
end
end
begin
s = S.new 0
s.x = 1
s.x.should eq 1 # OK
end
begin
a = [] of S
a << S.new 0
a[0].x = 1
a[0].x.should eq 1 # still 0
end
Here is an example:
require "spec"
struct S
property x : Int32
def initialize(@x)
end
end
begin
s = S.new 0
s.x = 1
s.x.should eq 1 # OK
end
begin
a = [] of S
a << S.new 0
a[0].x = 1
a[0].x.should eq 1 # still 0
end
a[0] is returning a copy since structs are pass by value.
See Structs - Crystal.
Related feature discussion: Iterate slice by reference (not by value) · Issue #14800 · crystal-lang/crystal · GitHub
I.e. you want:
a = [S.new(0)]
index = 0
itemp = a.to_unsafe + index
itemp.value.x = 1
a[0].x.should eq(1) # OK
Isn’t just better to update the array position with the new (locally changed) struct (with the new value) instead of this unsafe trickery?
Agreed. Mutable struct types will lead to headaches later. There will be times you will forget it’s passed and returned by value rather than by reference and you’ll lose a frustrating amount of time debugging it.
If it’s a struct, make it immutable. Give it the ability to return an updated copy if you need to, but make the instance immutable. If the object needs to accrue its own state directly, make it a class.
One of the nice things about Crystal is that you never need to update a call site when you swap between pass-by-value and pass-by-reference. It’s tedious in most other languages that have this distinction.
In Golang and other languages, only copy when there is assignment