In Crystal there are reference types and value types. For example, classes are reference types, and basic types like booleans, integers, and notably structs and tuples are value types.
When you pass an instance of the
Post class as an argument you are passing a reference to the post object. You’re always working with references. As in Java or Ruby. If the post instance is mutable through its API, the called method can change its state.
That is not possible with value types. If the post was an instance of a struct, then the whole thing is copied. You can be certain there is no way the called method can modify the state of the original struct in the caller regardless of whether there is API for it.
In C, structs are also copied like that. If you want to be able to modify a struct in the called function you need to add an indirection by passing a pointer to the struct.
I don’t like to use the terms pass by reference and pass by value because they are often confusing. For example, Java is a pass by value language in my definition of pass by value (you pass references by value, according to the JLS).
C is also pass by value, you may pass primitive types or pointers, but they go always by value.
To me, pass by reference means in practice that you can change the content of variables in the caller. Swap is the canonical example:
a = 1
b = 0
# Now a = 0, and b = 1 in the caller.
Perl is pass by reference.
(The documentation of Crystal mentions often “pass by reference”, though, so the core team may work with different definitions. But practical usage, regardless of the names, is the one explained above.)