-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Defs whose body consists of a single InstanceVar return that instance variable by reference, even if it has a value type. The majority of those methods are getters:
struct Foo
property x = 1
end
struct Bar
# the def body is just `@foo`, and `Foo` is a mutable struct
getter foo = Foo.new
# the def body is just `@y`, and `StaticArray(Int32, 1)` is a mutable value aggregate
getter y = StaticArray(Int32, 1).new(2)
end
bar = Bar.new
bar.foo.x = 3
bar.y[0] = 4
bar # => Bar(@foo=Foo(@x=3), @y=StaticArray[4])Owing to the ease of redefining things in Crystal, it is natural, for example, to add a decorator to a getter using previous_def:
require "log"
struct Bar
def foo
Log.info { "Bar#foo called" }
previous_def
end
endNow the def body is an Expressions instead of an InstanceVar, thus the semantics of the program have changed, and the last line now prints @foo=Foo(@x=1) printed. In the extreme case, y could have a massize size, and the code even suffers from long compilation times due to passing large structs around by value.
There are times when we cannot opt for setters on the instance variables (e.g. bar.y = StaticArray(Int32, 1).new(4) is a very different piece of code). This seems too big a pitfall in the language to ignore. How do we handle this while preserving the reference semantics? Is there perhaps a better way of writing such decorators?
Related: #13265 (this one is for Object#not_nil!), #3073
Add a 👍 reaction to issues you find important.