How to modify contents of enum in match using parent state

Hi,
I've run into a situation where I don't know how to properly structure my code to make the borrow checker happy and to keep it well structured.

I have a struct that contain many fields that are common to my objects. A small part of the state of these objects is represented by an enum, and depending on the value of this enum, the objects may contain some more variables (tread below). A minimal example below:

struct Vehicle {
    velocity: f32,
    //lots more common parameters
    animation_type: AnimationType,
}

enum AnimationType {
    Sled,
    Snowmobile { tread: f32 },
}

impl Vehicle {
    // Actually a slow function that I don't
    // want to call unnecessarily
    fn get_velocity(&self) -> f32 {
        return self.velocity;
    }
    fn increment_animation(&mut self, dt: f32) {
        match self.animation_type {
            AnimationType::Sled => {}
            AnimationType::Snowmobile { ref mut tread } => {
                // This works fine
                //*tread += dt * self.velocity;
                
                // Gives: cannot borrow `*self` as immutable because it is also borrowed as mutable
                *tread += dt * self.get_velocity()
            }
        }
    }
}

I want to call get_velocity() inside the match, but the borrow checker does not allow me to. I don't want to call it before the match as this is a slow function (example above chosen to keep it simple). In practice I have many branches here, and they all contain some unique variables that I want to update using the (immutable) state of self. It seems to me like it should be safe to use the state of self, and afterwards update e.g. tread, but it is not allowed.

Is there some recommended pattern for achieving what I want to do?

In scenarios like these, it's usually sufficient to copy with one match, then write with another (Rust Playground):

enum AnimationType {
    Sled,
    Snowmobile { tread: f32 },
}

struct Vehicle {
    animation_type: AnimationType,
    velocity: f32,
}

impl Vehicle {
    fn get_velocity(&self) -> f32 {
        self.velocity
    }

    fn increment_animation(&mut self, dt: f32) {
        match self.animation_type {
            AnimationType::Sled => {}
            AnimationType::Snowmobile { tread } => {
                let new_tread = tread + dt * self.get_velocity();
                match &mut self.animation_type {
                    AnimationType::Snowmobile { tread } => *tread = new_tread,
                    _ => unreachable!(),
                }
            }
        }
    }
}

It would be nice if there were an easier way of doing this, though.

You can't do this as-is, because you can't have simultaneous mutable and immutable borrows. If your slow read-only calculation doesn't require the tread piece of the state, then you can rewrite your get_velocity() method as a non-method function which explicitly takes only the fields it needs.

Split your structs along borrowing lines. Instead of trying to make get_velocity() a function on Vehicle, make it a function of the fields that aren't the enum, as their own separate struct. (This is the same thing as @H2CO3's suggestion to ‘explicitly take the fields it needs’, except with more setup, which will end up cleaner if many fields are needed.)

struct Vehicle {
    common: VehicleCommon,
    kind: VehicleKind,
}

struct VehicleCommon {
    velocity: f32,
    //lots more common parameters
}

enum VehicleKind {
    Sled,
    Snowmobile { tread: f32 },
}

impl VehicleCommon {
    fn get_velocity(&self) -> f32 {
        return self.velocity;
    }
}

impl Vehicle {
    fn increment_animation(&mut self, dt: f32) {
        match self.kind {
            VehicleKind::Sled => {}
            VehicleKind::Snowmobile { ref mut tread } => {
                *tread += dt * self.common.get_velocity();
            }
        }
    }
}

@LegionMammal978 Thanks for the workaround. It is ugly, but it works so I guess this is what I will have to do :).

@H2CO3 Thanks for the tip, indeed the equivalent of tread isn't used here. However, in my particular case this isn't practical as get_velocity() uses many of the fields in Vehicle, and get_velocity() is also used throughout the code without matches like here so it would make many other calls unnecessarily complex. Not inputting the struct but almost all the fields of the struct kind of defeats the purpose of having a struct at all.

(at)kpreid Thanks for the tip, indeed many fields are necessary. This seems messy though as this is a small part of the code, but it will affect everything touching the 'Vehicle' having to make this distinction between common and enums though there really isn't anything special about the enums. It also doesn't seem sustainable as when I do the same for the next enums I would need to split them out too and input separately..

struct Vehicle {
    non_enums: NonEnumPartOfVehicle,
    enum_1: Enum1,
    enum_2: Enum2,
..
}

struct NonEnums {
    velocity: f32,
    //lots more common parameters
}

enum Enum1 {
    Sled,
    Snowmobile { tread: f32 },
}

enum Enum2 {
    HomeMade,
    Stolen,
    StoreBought{price: f32},
}

impl Vehicle {
    fn function1(&self) -> f32 {
        self.NonEnumPartOfVehicle.function1(self.enum1, self.enum2)
    }
}

impl NonEnumPartOfVehicle {
    fn function1(&self, &enum1: Enum1, &enum2: Enum2,..) -> f32 {
        ...
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.