Search code examples
rustborrow-checkerownership

Why can't I edit these values outside the scope?


I am working on a ray tracer where I need to be able to change certain variables based on input on runtime. I have values such as CamDir and CamPos that need to be changed over runtime but keep reverting back to their default values. I think there's an issue with storing the variables back into the Renderer struct. It would make sense, as I'm new to programming and suck at understanding borrowing and ownership stuff. Here I'll include my code on GitHub, as well as a simpler example that somewhat faithfully recreates the situation I'm dealing with. I've been struggling with this issue for quite a while, so I'd appreciate help trying to fix my terrible code.

GitHub: https://github.com/KGL8/RayTracingPaper/tree/working_snapshot

Simplified Code Example:

#[derive(Debug)]
pub struct feed {
    foodId: i32,
    healthy: bool
}

impl feed {
    pub fn new(foodId: i32) -> feed {
        feed {
            foodId: foodId,
            healthy: true
        }
    }
    
    pub fn newValues(mut self) {
        self.healthy = false;
        self.foodId = 234567645;
    }
}

#[derive(Debug)]
pub struct animal {
    animalType: i32, // 0 = dog, 1 = cat
    foodType: feed,
    joy: i32         // 0% -> 100%
}

impl animal {
    pub fn new(animalType: i32, foodType: feed) -> animal {
        animal {
            animalType: animalType,
            foodType: foodType,
            joy: 50
        }
    }
    
    pub fn changePetFood(self) {
        self.foodType.newValues();
    }
}

pub fn walk(mut a: animal) {
    a.changePetFood();
    a.joy = 100;
}

fn main() {
    let food = feed::new(264369);
    let doggy = animal::new(0,food);
    
    println!("{:?}",doggy); 
    walk(doggy);
    println!("{:?}",doggy);
}

I've tried a whole combination of mutability, referencing, and passing as parameters, but nothing seems to work out...

Here is a tree that attempts to explain the structure I've set up for my code:

Main.rs -> [1] App.rs -> Renderer.rs -> Camera.rs -> App.rs -> Renderer.rs -> [Loop back to [1]]


Solution

  • You need to understand the distinction between a value and a reference.

    In Rust, when you pass a value as a parameter to a function, this moves the value (except for the basic primitive types and some variations around that, which are simply copied because it costs almost nothing). This means that now the value is not anymore in its initial location but in the parameter of the function (this is simplified, but the main idea is here): the function has consumed the value which is not available anymore in its initial location. For example, calling walk(doggy) consumes doggy and you cannot display it after this call.

    Instead, you should pass a reference to doggy in order to leave it in place but interact with it anyway. Because your intention is to mutate doggy, you have to pass a mutable (exclusive) reference: walk(&mut doggy) but duggy must have been declared as mutable to allow that (let mut doggy = ...).

    Of course, the parameter of walk() must be changed to a: &mut Animal.

    If you are OK with this till this point, then you should be able to follow the same way for the other errors (they are similar). You just have to known that self is a shortcut for self: Self, &self is a shortcut for self: &Self and &mut self is a shortcut for self: &mut Self, where Self represents the considered type for the implementation (Feed, Animal...)

    As a side note, you should consider the compiler warnings about the naming conventions for the types, variables, functions... And as stated in a comment, the integer animal_type can be replaced by an enum representing only the expected animals.

    #[derive(Debug)]
    pub struct Feed {
        food_id: i32,
        healthy: bool,
    }
    
    impl Feed {
        pub fn new(food_id: i32) -> Self {
            Self {
                food_id,
                healthy: true,
            }
        }
    
        pub fn new_values(&mut self) {
            self.healthy = false;
            self.food_id = 234567645;
        }
    }
    
    #[derive(Debug)]
    pub enum AnimalType {
        Dog,
        Cat,
    }
    
    #[derive(Debug)]
    pub struct Animal {
        animal_type: AnimalType,
        food_type: Feed,
        joy: i32, // 0% -> 100%
    }
    
    impl Animal {
        pub fn new(
            animal_type: AnimalType,
            food_type: Feed,
        ) -> Self {
            Self {
                animal_type,
                food_type,
                joy: 50,
            }
        }
    
        pub fn change_pet_food(&mut self) {
            self.food_type.new_values();
        }
    }
    
    pub fn walk(a: &mut Animal) {
        a.change_pet_food();
        a.joy = 100;
    }
    
    fn main() {
        let food = Feed::new(264369);
        let mut doggy = Animal::new(AnimalType::Dog, food);
    
        println!("{:?}", doggy);
        walk(&mut doggy);
        println!("{:?}", doggy);
    }
    /*
    Animal { animal_type: Dog, food_type: Feed { food_id: 264369, healthy: true }, joy: 50 }
    Animal { animal_type: Dog, food_type: Feed { food_id: 234567645, healthy: false }, joy: 100 }
    */