如何重复循环由项目属性确定的集合的子集?

时间:2017-08-26 21:53:16

标签: rust

我有一个由结构Manager拥有的对象集合。这些对象具有可选属性,例如printabledynamic。我想重复遍历所有可打印对象以打印它们并循环遍历所有动态对象以更新它们。我相当天真的实现是这样的:

struct Object {
    state: i32,
    printable: Option<i32>,
    dynamic: Option<i32>
}

struct Manager {
    objects: Vec<Object>,
}

impl Manager {
    fn print_objects(&self) {
        for o in &self.objects {
            if let Some(i) = o.printable {
                print!("{}: {}, ", i, o.state);
            }
        }
        println!();
    }

    fn update_objects(&mut self) {
        for o in &mut self.objects {
            if let Some(i) = o.dynamic {
                o.state += i;
            }
        }
    }
}

fn main() {
    let mut mgr = Manager{objects: Vec::new()};

    mgr.objects.push(Object{state: 0, printable: Some(10), dynamic: None});
    mgr.objects.push(Object{state: 0, printable: None, dynamic: Some(1)});
    mgr.objects.push(Object{state: 0, printable: Some(20), dynamic: Some(2)});

    for _ in 0..3 {
        mgr.update_objects();
        mgr.print_objects();
    }
}

此解决方案的缺点是需要遍历所有对象并检查每个对象是否有适当的标志。假设只有一小部分对象是动态的,我宁愿避免遍历所有对象。在C ++中,我只是创建一个指向动态对象的指针列表并循环遍历它。试图这样做:

struct Manager<'a> {
    objects: Vec<Object>,     // objects owned by Manager
    dynamic: Vec<&'a Object>  // references to dynamic objects
}

impl<'a> Manager<'a> { /* ... */ }

fn main() {
    let mut mgr = Manager{objects: Vec::new(), dynamic: Vec::new()};

    mgr.objects.push(Object{state: 0, printable: Some(10), dynamic: None});
    mgr.objects.push(Object{state: 0, printable: None, dynamic: Some(1)});
    mgr.objects.push(Object{state: 0, printable: Some(20), dynamic: Some(2)});

    mgr.dynamic.push(&mgr.objects[1]);
    mgr.dynamic.push(&mgr.objects[2]);

    for _ in 0..3 {
        mgr.update_objects();  // can't mutably borrow because of reference held by mgr.dynamic
        mgr.print_objects();
    }
}

这似乎是一个非常基本的问题,我无法在Manager.objects中保留对元素的引用,同时可以相互借用它。因此,我开始怀疑我的整个方法。在Rust中实现这样的模式是否有惯用的方法?

我相信保留对象的引用会阻止我改变它们,但我很乐意学习一种解决方法。未来的目标是在更新期间更改printabledynamic,但我想在解决该步骤之前找出基本内容。

1 个答案:

答案 0 :(得分:2)

struct Manager<'a> {
    objects: Vec<Object>,     // objects owned by Manager
    dynamic: Vec<&'a Object>  // references to dynamic objects
}

这不起作用,并已在Why can't I store a value and a reference to that value in the same struct?中进行了详细讨论。

一种解决方案是拥有单个值的多个所有者,称为共享所有权。一个实现是Rc

您还希望具有与程序范围无关的可变性,而是与运行时特性相关联,这可以通过内部可变性启用。一个实现是RefCell

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Object {
    state: i32,
    printable: Option<i32>,
    dynamic: Option<i32>,
}

#[derive(Debug, Default)]
struct Manager {
    objects: Vec<Rc<RefCell<Object>>>,
    printable: Vec<Rc<RefCell<Object>>>,
    dynamic: Vec<Rc<RefCell<Object>>>,
}

impl Manager {
    fn add(&mut self, state: i32, printable: Option<i32>, dynamic: Option<i32>) {
        let obj = Object { state, printable, dynamic };
        let obj = Rc::new(RefCell::new(obj));

        self.objects.push(obj.clone());

        if printable.is_some() {
            self.printable.push(obj.clone())
        }

        if dynamic.is_some() {
            self.dynamic.push(obj.clone())
        }
    }

    fn print_objects(&self) {
        for o in &self.printable {
            let o = o.borrow();
            if let Some(i) = o.printable {
                print!("{}: {}, ", i, o.state);
            }
        }
        println!();
    }

    fn update_objects(&mut self) {
        for o in &self.dynamic {
            let mut o = o.borrow_mut();
            if let Some(i) = o.dynamic {
                o.state += i;
            }
        }
    }
}

fn main() {
    let mut mgr = Manager::default();

    mgr.add(0, Some(10), None);
    mgr.add(0, None, Some(1));
    mgr.add(0, Some(20), Some(2));

    for _ in 0..3 {
        mgr.update_objects();
        mgr.print_objects();
    }
}

您仍然存在将项目从一个组转换到另一个组的所有固有问题。