rustborrow-checker

Why `freq` is no longer valid


I'm struggling with the borrow checker and hope anyone can give me a clue.

The relevant code is below:

// Doc: https://docs.rs/strum/latest/strum/trait.IntoEnumIterator.html
use strum::IntoEnumIterator;

// Doc: https://docs.rs/strum/latest/strum/derive.EnumIter.html
use strum_macros::EnumIter;

#[derive(EnumIter, Debug)]
pub enum Frequency {
    Freq1m,
    Freq1h,
    Freq1d,
}

impl Frequency {
    pub fn value(&self) -> i32 {
        match self {
            Frequency::Freq1m => 60,
            Frequency::Freq1h => 3600,
            Frequency::Freq1d => 86400,
        }
    }

    pub fn display_name(&self) -> &'static str {
        match self {
            Frequency::Freq1m => "1m",
            Frequency::Freq1h => "1h",
            Frequency::Freq1d => "1d",
        }
    }
}

impl PartialEq for Frequency {
    fn eq(&self, other: &Self) -> bool {
        self.value() == other.value()
    }
}

pub struct CodeExample {
    frequency: Frequency,
}

impl CodeExample {
    fn ui(&mut self, ui: &mut egui::Ui) {
        for freq in Frequency::iter() {                                          // move occurs
            ui.selectable_value(&mut self.frequency, freq, freq.display_name()); // borrow of moved value
        }
    }
}

After trial and error, I know that if I add the Clone and Copy trait to my Frequency, it will work. But why? My understanding is that the move in for freq in Frenquency::iter() should be fine, right? The later borrow seems valid to me. What am I missing?


Solution

  • My understanding is that the move in for freq in Frenquency::iter() should be fine, right? The later borrow seems valid to me. What am I missing?

    Arguments to functions in rust are evaluated strictly from left to right. So in

    ui.selectable_value(&mut self.frequency, freq, freq.display_name());
    

    because freq is not Copy, it is moved into the function parameters which renders it invalid, then you try to borrow the now invalid freq in order to compute its display_name. Hence use of a moved value.

    Either make Frequency Copy (it's a trivial enum) or store freq.display_name() in a local before calling the method. Copy works because a value which is Copy is... copied, rather than moved. So freq remains valid after it is "moved" as a parameter, rather than become invalid.