Understanding Rust Error E0509
Introduction to Rust Error E0509
In Rust, Error E0509 occurs when there's an attempt to move a value out of a struct, enum, or tuple whose type implements the Drop trait. The Drop trait is used to specify what happens when a value goes out of scope, and the error is caused by the fact that the destructor might need the moved fields.
Example of Erroneous Code
struct FancyNum {
num: usize
}
struct DropStruct {
fancy: FancyNum
}
impl Drop for DropStruct {
fn drop(&mut self) {
// Destruct DropStruct, possibly using FancyNum
}
}
fn main() {
let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
let fancy_field = drop_struct.fancy; // Error E0509
println!("Fancy: {}", fancy_field.num);
// implicit call to `drop_struct.drop()` as drop_struct goes out of scope
}
Explaining the Error
In the code above, the Drop trait is implemented on the DropStruct type. However, the error occurs because the fancy_field is trying to move the FancyNum value out of the DropStruct instance (drop_struct). This would make it impossible to run the destructor implemented for the Drop trait.
How to Fix Error E0509
To fix this error, you can create a reference to the fields of the struct, enum, or tuple using the ref keyword. Here's the fixed code using the ref keyword:
struct FancyNum {
num: usize
}
struct DropStruct {
fancy: FancyNum
}
impl Drop for DropStruct {
fn drop(&mut self) {
// Destruct DropStruct, possibly using FancyNum
}
}
fn main() {
let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
let ref fancy_field = drop_struct.fancy; // No more errors!
println!("Fancy: {}", fancy_field.num);
// implicit call to `drop_struct.drop()` as drop_struct goes out of scope
}
Using Ref in a Match Expression
The same technique can be used in the arms of a match expression to prevent Error E0509. Here's an example:
struct FancyNum {
num: usize
}
enum DropEnum {
Fancy(FancyNum)
}
impl Drop for DropEnum {
fn drop(&mut self) {
// Destruct DropEnum, possibly using FancyNum
}
}
fn main() {
// Creates an enum of type `DropEnum`, which implements `Drop`
let drop_enum = DropEnum::Fancy(FancyNum{num: 10});
match drop_enum {
// Creates a reference to the inside of `DropEnum::Fancy`
DropEnum::Fancy(ref fancy_field) => // No error!
println!("It was fancy-- {}!", fancy_field.num),
}
// implicit call to `drop_enum.drop()` as drop_enum goes out of scope
}