Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Rust—缓存某些不可变数据的视图的惯用方法_Rust - Fatal编程技术网

Rust—缓存某些不可变数据的视图的惯用方法

Rust—缓存某些不可变数据的视图的惯用方法,rust,Rust,下面是一个简单结构World的示例代码,其中包含一个Objects的向量,为每个Object分配了一个类别 #[derive(PartialEq, Debug)] enum Category { A, B, C, D } #[derive(Debug)] struct Object { pub someData: f64, pub category: Category //... } struct World { pub objects: Vec<

下面是一个简单结构
World
的示例代码,其中包含一个
Object
s的向量,为每个
Object
分配了一个类别

#[derive(PartialEq, Debug)]
enum Category {
    A, B, C, D
}

#[derive(Debug)]
struct Object {
    pub someData: f64,
    pub category: Category
    //...
}

struct World {
    pub objects: Vec<Object>,
    //...
}

impl World {
    pub fn new(objects: Vec<Object>) -> Self {
        World { objects }
    }

    pub fn getObjectsOfCategoryA(&self) -> Vec<&Object> {
        self.objects.iter().filter(|x| x.category == Category::A).collect()
    }
}
这让我觉得不够理想,因为:

  • 我们需要更改主
    对象
    向量的存储模式
  • 这并不能直观地向代码读者表明
    objectsOfCategoryA
    仅仅是
    对象的一个视图
  • 如果
    对象
    意外发生变异,则此操作将自动失败。理想情况下,如果在构建了
    World
    之后有任何东西试图改变
    对象
    ,我希望出现编译错误
  • 如果有什么方法可以让
    objectsOfCategoryA
    成为
    Vec
    ,我会觉得这是“正确的”,但从我所做的研究来看,这似乎是不可能的

    我是新手,所以我很可能是从OOP的角度来看待这个问题的。有人能指出一种实现这种缓存的惯用方法吗?

    我们不容易生锈,但在这里,我们根本不需要存储引用。我们所需要的只是对象的索引列表
    get\u objects\u of_category\u a()
    只需将索引映射到引用即可

    由于对象列表是不可变的,为了简单起见,我选择在构造函数中构建索引列表。它也可以根据需要进行初始化

    struct World {
        objects: Vec<Object>,
        objects_of_category_a: Vec<usize>,
        //...
    }
    
    impl World {
        pub fn new(objects: Vec<Object>) -> Self {
            let objects_of_category_a = objects
                .iter()
                .enumerate()
                .filter(|&(_, x)| x.category == Category::A)
                .map(|(i, _)| i)
                .collect();
            World {
                objects,
                objects_of_category_a,
            }
        }
    
        pub fn get_objects_of_category_a(&self) -> Vec<&Object> {
            self.objects_of_category_a.iter().map(|&i| &self.objects[i]).collect()
        }
    }
    
    然而,由于这是一个递归数据结构,我们需要某种形式的间接寻址,我在这里使用
    Box
    实现了这种间接寻址。对于平衡树,它还意味着查找元素将在
    O(logn)
    中运行,而索引
    Vec
    则在
    O(1)
    中运行


    另一种选择是如上所述将对象存储在
    Vec
    中,并将索引存储在树中。

    您希望
    类别::a
    对象的缓存可以是
    Vec
    类型。这不是惯用的方法,需要修补才能工作。其次是一个类型为
    Option
    的延迟计算缓存。如果世界被宣布为

    struct World>,
    //...
    }
    
    您可以将其初始化为
    World{objects,None}
    ,然后当您需要获取
    Category::A
    的对象时,您可以迭代
    Vec
    并填充缓存字段(注意:这需要mut引用,这可以通过内部可变性避免)

    pub fn getObjectsOfCategoryA(&'a mut self)->&'a Vec{
    如果self.category\u a.是\u none(){
    self.category_a=Some(self.objects.iter().filter(|x | x.category==category::a.collect());
    }
    self.category_a.as_ref().unwrap()
    }
    
    您甚至可以通过包装World的
    objects.push()
    来正确更新缓存,从而允许对象发生变异,如下所示

    //impl-World{
    // ...
    pub fn push_inner(&'a mut self,obj:Object){
    自我对象推送(obj);
    如果self.objects.last().unwrap().category==category::A{
    如果让一些(类别a)=&mut self.category\u a{
    类别推送(self.objects.last().unwrap())
    }
    }
    }
    

    是指向用于测试此功能的完整代码的链接。

    确保
    对象
    s不发生变异的一个合理方法是将其设置为非发布字段。如果您希望允许用户读取
    对象
    s,您可以使用
    对象(&self)->&Vec
    方法。如果希望用户具有写访问权限,则可以使用
    objects\u mut(&mut self)->&mut-Vec
    这会使缓存无效。@asky这是一个很好的观点-我会这么做,但这仍然不能回答如何最好地完成缓存部分。是的,这就是为什么它是一个注释,而不是一个答案!你说世界上的
    对象
    是不可变的,但既然它是一个发布字段,你怎么能确定呢?例如,如果这个API的使用者声明了
    World
    作为
    mut
    ,对象是可变的。回答得好,但是如果对象不是
    Vec
    ,而是更复杂的数据结构,不支持直接索引,那该怎么办?在我的例子中,它是一个自定义树。(如果答案是“不要使用那种数据结构”,或者“向树添加索引”)我想我认为这是完全有效的;)嗯-要求可变引用对我来说似乎是不可接受的,在我的实现中,整个世界的要点是它是不可变的,并且可以在线程之间共享。因此,为了使内部的可变性发挥作用,我们正在考虑要求互斥体使此解决方案可行。在这一点上,
    Arc
    解决方案感觉更好,即使呃,我还是不喜欢。。。
    struct World {
        objects: Vec<Object>,
        objects_of_category_a: Vec<usize>,
        //...
    }
    
    impl World {
        pub fn new(objects: Vec<Object>) -> Self {
            let objects_of_category_a = objects
                .iter()
                .enumerate()
                .filter(|&(_, x)| x.category == Category::A)
                .map(|(i, _)| i)
                .collect();
            World {
                objects,
                objects_of_category_a,
            }
        }
    
        pub fn get_objects_of_category_a(&self) -> Vec<&Object> {
            self.objects_of_category_a.iter().map(|&i| &self.objects[i]).collect()
        }
    }
    
    enum Path {
        /// The target is the current node.
        Stop,
    
        /// Set the target to the current node's left subnode.
        Left(Box<Path>),
    
        /// Set the target to the current node's right subnode.
        Right(Box<Path>),
    }