为什么我不能在作用域之外编辑这些值?

huangapple go评论55阅读模式
英文:

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

问题

我正在开发一个光线追踪器,在其中需要根据运行时的输入来更改某些变量。我有诸如 CamDirCamPos 这样的值,需要在运行时进行更改,但会不断恢复到它们的默认值。我认为在将变量存储回 Renderer 结构中存在问题。这似乎很有道理,因为我是编程新手,不太懂借用和所有权的内容。在这里,我将包括我的 GitHub 代码以及一个更简单的示例,其中基本上重现了我正在处理的情况。我已经为这个问题挣扎了相当长时间,所以我会感激帮助来修复我的糟糕代码。

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

简化的代码示例:

#[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(&mut 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);
}

我尝试了多种可变性、引用和作为参数传递的组合,但似乎没有什么效果...

这里是我为代码设置的结构的树状图:

Main.rs -> 1 App.rs -> Renderer.rs -> Camera.rs -> App.rs -> Renderer.rs -> [回到1]

英文:

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]

答案1

得分: 2

你需要理解引用之间的区别。

在Rust中,当你将一个值作为参数传递给函数时,这会移动该值(除了基本的原始类型和一些变化,它们会被简单地复制,因为几乎没有成本)。
这意味着现在该值不再位于其初始位置,而是位于函数的参数中(这是一个简化,但主要思想在这里):函数已经消耗了该值,该值不再在其初始位置可用。
例如,调用walk(doggy)会消耗doggy,在此调用之后,你无法再显示它。

相反,你应该传递一个对doggy引用以使其保留在原地但仍然与之交互。
因为你的意图是对doggy进行改变,所以你必须传递一个可变(独占)引用:walk(&mut doggy),但必须声明duggy为可变以允许这样做(let mut doggy = ...)。

当然,walk()的参数必须更改为a: &mut Animal

如果你对此点感到满意,那么你应该能够以同样的方式解决其他错误(它们类似)。
你只需知道selfself: Self的快捷方式,&selfself: &Self的快捷方式,&mut selfself: &mut Self的快捷方式,其中Self表示要实现的类型(FeedAnimal...)。

作为一则附带说明,你应该考虑编译器关于类型、变量、函数命名约定的警告...
并且如评论中所述,整数animal_type可以被一个代表只包含期望动物的enum替代。

#[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 }
*/
英文:

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 }
*/

答案2

得分: 0

我将为您翻译代码部分:

// 我将专注于这个函数。
pub fn newValues(mut self) {
    self.healthy = false;
    self.foodId = 234567645;
}

// 这个函数基本上什么都不做。它将 `self` 移入函数,对其进行更改,然后丢弃它。它没有办法让这些信息离开函数。
// 如果您想在更改它们后能够观察这些值,您需要要么返回 `self`...
pub fn newValues(mut self) -> Self {
    self.healthy = false;
    self.foodId = 234567645;
    self
}
// ... 或者您需要获取对 self 的可变引用。
pub fn newValues(&mut self) {
    self.healthy = false;
    self.foodId = 234567645;
}

// 请注意,参数列表中的 `&mut self` 是特殊语法,仅适用于 `self`,等同于 `self: &mut Self`。当您将其应用于 `walk` 时,它会像这样。
pub fn walk(a: &mut animal) {
    a.changePetFood();
    a.joy = 100;
}

// 这些更改是为了使您的示例编译通过,但是您的完整项目似乎在使用 [`Copy` 类型][1]。当您按值获取复制类型,例如使用 `fn newValues(mut self)` 时,它将值复制到函数中,而不是移动它。这意味着原始值仍然有效,但它不会看到您在函数内部进行的更改,因为那是对副本进行的。

// 修复后的完整示例如下。
#[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(&mut self) {
        self.foodType.newValues();
    }
}

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

fn main() {
    let food = feed::new(264369);
    let mut doggy = animal::new(0, food);

    println!("{:?}", doggy);
    walk(&mut doggy);
    println!("{:?}", doggy);
}

我的建议是从您的类型中删除 Copy,然后在您遇到错误的地方明确调用 clone() 或传递引用,并更改函数签名以匹配。

英文:

I'll focus on this function.

pub fn newValues(mut self) {
    self.healthy = false;
    self.foodId = 234567645;
}

This function does basically nothing. It moves self into the function, changes it, and then drops it. It has no way for this information to leave the function.

If you want to be able to observe these values after you change them, you need to either return self...

pub fn newValues(mut self) -> Self {
self.healthy = false;
self.foodId = 234567645;
self
}

... or you need to take a mutable reference to self.

pub fn newValues(&mut self) {
    self.healthy = false;
    self.foodId = 234567645;
}

Note that &mut self in the argument list is special syntax that only works with self, and is the same as self: &mut Self. When you apply this to walk, it will look like this.

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

These changes are necessary to make your example compile, but your full project looks to be using Copy types. When you take a copy type by value, such as with fn newValues(mut self), it copies the value into the function instead of moving it. This means the original value will still be valid, but it won't see the changes you make inside the function, since that's being done to a copy.

Your full example fixed looks like this.

#[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(&mut self) {
        self.foodType.newValues();
    }
}

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

fn main() {
    let food = feed::new(264369);
    let mut doggy = animal::new(0, food);

    println!("{:?}", doggy);
    walk(&mut doggy);
    println!("{:?}", doggy);
}

My advice would be to remove Copy from your types, then either explicitly call clone() or pass references wherever you get errors, and change your function signatures to match.

huangapple
  • 本文由 发表于 2023年6月29日 04:22:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576501.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定