【问题标题】:Change borrowed value when modifying original value修改原值时更改借入值
【发布时间】:2018-02-01 06:30:00
【问题描述】:

首先,这是我正在处理的代码的简化版本:

struct Object {
    size: f32
}

impl Object {
    fn on_event(&mut self) {
        self.size += 1.0;
        println!("Object notified. New size: {}.", self.size);
    }
}

struct Manager<'a> {
    objects: Vec<&'a mut Object>
}

impl<'a> Manager<'a> {
    fn add(&mut self, obj: &'a mut Object) {
        self.objects.push(obj);
    }
    fn notify_objects(&mut self) {
        for i in range(0u, self.objects.len()) {
            let ref mut obj = *self.objects.get_mut(i);
            obj.on_event();
        }
    }
}

fn main() {
    let mut obj1 = Object { size: 1.0 };
    let mut obj2 = Object { size: 2.0 };
    let mut obj3 = Object { size: 3.0 };

    let mut manager = Manager { objects: Vec::new() };
    manager.add(&mut obj1);
    manager.add(&mut obj2);
    manager.add(&mut obj3);

    obj1.size = 25.0;
    println!("before {}", obj1.size); // should print 25

    manager.notify_objects();

    println!("after {}", obj1.size); should print 26
}

所以我喜欢创建可变对象并将它们添加到管理器中,但我应该能够修改原始对象,如代码所示。

【问题讨论】:

  • 建议将您的问题缩减为尽可能少的代码来演示问题。

标签: pointers rust mutable


【解决方案1】:

仅供参考,尽管公认的答案是完全正确的,但您可能会遇到许多情况,其中从其他位置访问对象不如简单的 mutable-borrow-back-from-owner 方便。

例如,如果您正在执行某种观察者模式,其中一个位置正在使用一个对象,而另一个位置正在观察对象的状态变化并在它发生变化时运行一些东西。

在这些情况下,您可能最好使用 RefCell (http://doc.rust-lang.org/std/cell/struct.RefCell.html);您可以在多个位置拥有对对象的引用,并使用“try_borrow()”获取临时实例以在特定子范围内查看。

在您的示例中,这可能有点矫枉过正,但当您遇到更复杂的情况时,这是对对象进行“多次引用”的一般解决方案。

【讨论】:

    【解决方案2】:

    您的代码 does not compile 包含以下错误:

    <anon>:38:5: 38:21 error: cannot assign to `obj1.size` because it is borrowed
    <anon>:38     obj1.size = 25.0;
                  ^~~~~~~~~~~~~~~~
    <anon>:34:22: 34:26 note: borrow of `obj1.size` occurs here
    <anon>:34     manager.add(&mut obj1);
    

    问题是,您不能修改已借用的对象(这正是“借用”一词的语义,不是吗?)。当您获取obj1 的可变引用并将其放入结构中时,您实际上阻止了对obj1 的所有访问,直到此可变引用超出范围。但是因为当您尝试重新分配 obj1.size 时它还没有这样做,所以您会收到此错误。

    This does work:

    fn main() {
        let mut obj1 = Object { size: 25.0 };
        let mut obj2 = Object { size: 2.0 };
        let mut obj3 = Object { size: 3.0 };
    
        obj1.size = 25.0;
        println!("before {}", obj1.size); // should print 25
    
        {
            let mut manager = Manager { objects: Vec::new() };
            manager.add(&mut obj1);
            manager.add(&mut obj2);
            manager.add(&mut obj3);
    
            manager.notify_objects();
        }
    
        println!("after {}", obj1.size); // should print 26
    }
    

    这里manager 出现在它自己的范围内。因为该范围严格位于对obj1 的访问之间,所以它工作正常。

    如果您在借用时绝对必须访问这些数据,you have to go through the structure 本身:

    fn main() {
        let mut obj1 = Object { size: 1.0 };
        let mut obj2 = Object { size: 2.0 };
        let mut obj3 = Object { size: 3.0 };
    
        let mut manager = Manager { objects: Vec::new() };
        manager.add(&mut obj1);
        manager.add(&mut obj2);
        manager.add(&mut obj3);
    
    
        {
            let obj1 = manager.objects.get_mut(0);  // returns mutable reference
            obj1.size = 25.0;
            println!("before {}", obj1.size); // should print 25
        }
    
        manager.notify_objects();
    
        {
            let obj1 = manager.objects.get(0);  // returns immutable reference
            println!("after {}", obj1.size); // should print 26
        }
    }
    

    【讨论】:

    • 感谢您的快速回答!不知道我可以像这样创建一个新的范围。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    相关资源
    最近更新 更多