My proposal here, is to make the “move everything out of self” being the first step of any drop
function body, making writing such a thing safer and intuitive. For this purpose, we can always do a pattern match in the calling site. As this requirement need to change the way we call a function, we need a new ABI for the calling convention.
the destruction-call
ABI
trait Drop {
extern "destruction-call" fn drop(self);
}
Demostration:
enum MyEnum {
Variant1{ field: String },
Variant2(String)
}
impl Drop for MyEnum {
extern "destruction-call" fn drop(self) {
match self {
MyEnum::Variant1{ field } => println!("variant1: {}", field),
MyEnum::Variant2(s) => println!("variant2: {}", s)
}
}
}
let v = MyEnum::Variant2("123".into());
v.drop();
is desugar (not quite; magics needed) to
fn drop_myenum_variant1(field: String) {
println!("{}", field);
}
fn drop_myenum_variant2(s: String) {
println!("{}", s);
}
let v = MyEnum::Variant2("123".into());
match v {
MyEnum::Variant1{ field=field } => drop_myenum_variant1(field),
MyEnum::Variant2(s) => drop_myenum_variant2(s),
};
The destruction-call
ABI following the rules below:
- receive a single argument
- if the argument type is an enum, the function body must be a single
match
statement on its argument - in each match arms, the variables in the patterns are the things to be moved to the stack frame
- if the argument type is a struct, a single armed match statement is assumed and is optional (if not written in a match, the
self.field
path will be required, as it is today) - the
match
will be performed in the caller site
Magics
The only magic here is the need to create multiple code entries and count as a single “function”. The calling site destruction is just sugar.
Meanwhile, the new ABI can be used to declare other functions as well. So this “magic” makes Drop
strictly less magical. Now the Drop
trait is only magic in the sense it is called automatically.