Destructors
Types can define a method to run when before they are dropped, known as a
"destructor". Destructors are defined by implementing the Drop
trait from the
std.drop
module like so:
import std.drop.Drop
import std.stdio.STDOUT
class Person {
let @name: String
}
impl Drop for Person {
fn mut drop {
STDOUT.new.print("dropping {@name}")
}
}
class async Main {
fn async main {
Person { @name = 'Alice' }
}
}
If you run this program, the output is "dropping Alice".
The drop
method is always a mutable and private methods. If you try to
implement it using fn pub drop
or fn drop
, you'll run into a compile-time
error.
Escaping references
Destructors are mutable methods, which may result in a value that's to be
dropped escaping the drop
call:
import std.drop.Drop
import std.stdio.STDOUT
class Person {
let @name: String
let @people: mut Array[ref Person]
}
impl Drop for Person {
fn mut drop {
@people.push(self)
STDOUT.new.print("dropping {@name}")
}
}
class async Main {
fn async main {
let people = []
let person = Person { @name = 'Alice', @people = people }
}
}
In such cases, a runtime panic is produced:
dropping Alice
Stack trace (the most recent call comes last):
/var/home/yorickpeterse/Downloads/test.inko:19 in main.Main.main
/var/home/yorickpeterse/Downloads/test.inko:4 in main.Person.$dropper
Process 'Main' (0x56074450f210) panicked: can't drop a value of type 'Person' as it still has 1 reference(s)
In practice you're unlikely to run into cases such as this, but it's worth keeping in mind.