Here is a basic Spaceship struct, with a couple members.
We can construct it using its constructor function, which has the same name and was automatically generated.
All structs are mutable by default, more on that in the Mutability section below.
We can specify a custom constructor for our struct.
We just need to give it the same name as the struct. 0
Inside the constructor, we must call either another constructor or constructor<T>.
When we create a mutable struct, we get the owning reference to it. When the owning reference disappears, the struct is automatically deallocated (via drop, explained below).
There are other kinds of references (constraint, borrow, weak), References explains more.
Ownership is also found in C++ (unique_ptr), Rust, and Cyclone.
C also has "conceptual" ownership, in that we must track ownership without the language's help, to know when to free a struct.
Vale's ownership has the flexibility of C++'s unique_ptr without the mutability and aliasing restrictions of Rust and Cyclone, see References to learn how.
Every mutable struct has exactly one owning reference pointing to it.
We can make another reference to a struct with the & symbol. It will be a non-owning reference. 3 This is called a lend.
In this example, the type of owningRef is Spaceship, and the type of nonOwningRef is &Spaceship.
A local can give up the owning reference. Afterwards, that local is gone, and we cannot use it. This is called a move.
This is used to influence when the struct is dropped, to keep it alive for longer or destroy it sooner.
In this example, the a local is giving up the owning reference, and we're putting it into the b local.
It works the same way when passing an owning reference to a function.
Rather than thinking "a is an owning reference", think instead that a is a local that currently contains an owning reference.
On the next line, a is destroyed, but the owning reference that it contained still lives on (inside b).
When an owning reference disappears, the struct is automatically deallocated. Vale does this by inserting a call to drop. 5
The drop function is automatically generated for each struct and interface.
Here we can see where the implicit call to drop is.
drop is called when the owning reference goes away, which in this case is ship.
We can specify our own drop function. A custom drop could be used to:
Rule of thumb: if something must happen at some point in the future, put it in a drop function. Vale will make sure that it's not forgotten. 7
Drop functions also appear in C++ ("destructors") and Rust. Vale's drop functions are like those but more flexible: they can return values and even take extra parameters. In those cases, they must be called manually. See The Next Steps for Single Ownership and RAII for more!
The destruct keyword is syntactic sugar for a "move destructure" pattern, see Pattern Matching for more.
This is an incredibly powerful pattern, see The Next Steps for Single Ownership and RAII for more.
By default, structs are mutable. We can make immutable structs with the imm keyword.
After construction, an immutable struct cannot be changed at all.
Because of that, we can have multiple owning references to it, like Java or Python. 8
Vale also automatically derives the functions println, str, hash, ==, and more.
Immutable structs cannot have drop functions. 9
Small immutable structs (32b or less) are copied and passed by-value. Larger objects use SNRC (strategic nonatomic reference counting) to free themselves.
See The Next Steps for Single Ownership and RAII for the reasoning behind this.
These are planned features for Vale. See the roadmap for plans!
We normally call a function by name, such as the Spaceship("Serenity", 2) above. However, if the code is expecting a certain type, it can automatically call the constructor of the expected type.
This saves a lot of typing when calling functions.