In Ruby, it is generally considered good practice to use getters and setters (e.g., attr_reader, attr_accessor) rather than directly accessing instance variables with @var. This approach is believed to make the code more readable and maintainable.
However, I am wondering if this is always the case when using Structs in Crystal.
Since Struct is a value type, calling a getter or setter might copy the entire Struct.
Here are my questions:
Is it common in Crystal to access struct instance variables directly with @var?
Or is it still preferable to use getters and setters for clarity and consistency?
Or is this concern unnecessary because such optimizations are handled by the compiler at compile time?
Or, in the real world, are the effects of these differences so small that we don’t need to care?
How do you, as a Crystalist, prefer to write your code in cases like this?
getters are fine. 1 line methods that return ivars are actually inlined.
This rule was added to be able to do:
config.host.port = value
Even if config and host are struct, the above will change the port value of the config. Which is a bit odd if you consider structs are value types. But it’s a convenient rule.
There are a couple of discussions about this feature. It actually isn’t 100% sound. There are some (edge) cases such as assignment operators where this implicit inlining does not work.
Even a comment line inside the method body will break the inline IIRC.
I agree it’s not sound. Almost a quirk of the language from its early inception.
When I was trying to analyze more formally the type of a method I stumble on this not being able to be expressed in the return type with the current expressiveness.
TIL! This is honestly really cool. When you can make the language performant for the way code is written rather than forcing people to make choices around the performant thing, that’s great stuff.
This is one thing I feel like the JavaScript got correct. JS VM developers optimize around code that already exists rather than trying to convince the community to optimize it themselves.
This might explain some weird things I’ve seen. Would @[AlwaysInline] help here?
I think it depends on the situation whether you should use getters to access instance variables, but I feel like experienced engineers usually prefer using getters.
So, the inlining of getters that call structs has been a feature of Crystal from the beginning.
There are some deep edge cases, but the most common use cases have already been optimized.
If you don’t mind, could you tell me which part of the Crystal source code is responsible for the optimization of “inlining when a struct is called by a getter”? It may be difficult to understand correctly, but I would like to take a closer look at this fascinating language.
If it’s just a simple getter, use getter within object is unnecessary, even sacrificing readability, confusing with local variables, use ivar is more clearly.
@zw963
To be clear, I’m not saying that you should use getters instead of @. What I’m saying is that there seems to be a tendency among Rubyists to prefer using getters. I admit that I use getters a lot myself, but I’m not sure if it’s always the right choice. Conventions can be wise, but in this case, it might just be something I’m following without much thought.