lotsoftools

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
}