Skip to content

Decorating getters that return structs by reference #16315

@HertzDevil

Description

@HertzDevil

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
end

Now 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions