我如何根据向量中项目的信息来修改向量?

问题描述 投票:2回答:1

我如何基于Vec中项目的信息来修改Vec,而又不具有对向量的不变和可变引用?

我试图创建一个最小的示例来说明我的特定问题。在我的实际代码中,Builder结构已经是其他答案提出的中间结构。具体而言,我认为其他问题不会回答这个问题,因为:

假设我有一个项目定义的列表,其中项目是一个字符串,Item的嵌套列表,或指示应将一个新项目添加到正在处理的项目列表中:

enum Item { Direct(String), Nested(Vec<Item>), New(String), }

还有一个构建器,它包含一个Vec<Item>列表,并在指定的索引处构建一个项:

struct Builder { items: Vec<Item>, } impl Builder { pub fn build_item(&mut self, item: &Item, output: &mut String) { match item { Item::Direct(v) => output.push_str(v), Item::Nested(v) => { for sub_item in v.iter() { self.build_item(sub_item, output); } } Item::New(v) => self.items.push(Item::Direct(v.clone())), } } pub fn build(&mut self, idx: usize, output: &mut String) { let item = self.items.get(idx).unwrap(); self.build_item(item, output); } }

由于错误而无法编译:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable --> src/main.rs:26:9 | 25 | let item = self.items.get(idx).unwrap(); | ---------- immutable borrow occurs here 26 | self.build_item(item, output); | ^^^^^----------^^^^^^^^^^^^^^ | | | | | immutable borrow later used by call | mutable borrow occurs here error: aborting due to previous error For more information about this error, try `rustc --explain E0502`.

我不知道如何基于其中一项的信息(即[的不可变借项]来修改Builder结构(即对items进行可变引用)) C0])。

这里是代码的self.items

使用self.items

@@ Stargateur建议我尝试克隆playground example中的项目。尽管这确实可行,但由于性能原因,我一直尝试不克隆项目。

UPDATE:我没有在Clone中添加build()修改功能,而是在我的真实代码中实现了Vec<Item>方法,并克隆了与上述示例Item::New方法等效的值。当我执行clone()build()时,性能下降了12倍。我将继续寻找其他解决方案。问题是,我对Rust还是比较陌生,甚至不确定使用不安全的代码也不确定如何修改规则/做其他事情。

有效的代码(self.items.get(idx).unwrap().clone()

self.items.get(idx).unwrap()

并更改playground首先克隆该项目:

impl Clone for Item { fn clone(&self) -> Self { match self { Item::Direct(v) => Item::Direct(v.clone()), Item::Nested(v) => Item::Nested(v.clone()), Item::New(v) => Item::New(v.clone()), } } }

vector rust borrow-checker
1个回答
0
投票
[每次遇到这样的问题(使用Rust时,您会相对经常遇到),主要目标应该是将需要不可变借位的代码与要求可变借位的代码隔离开。如果无法避免从build中的 let item = self.items.get(idx).unwrap().clone(); vec借用(即,您不能将项目移出items或复制/克隆它),并且您必须将此项目的引用传递给build,则可能想考虑将您的self.items函数重写为

not变异build_item。在这种情况下,build_item只会在self的末尾附加新项目,这使我们可以进行有趣的重构:而不是让build_item修改self.items,而是使其返回要添加到原始项目的项目。向量,然后让caller将新生成的项添加到build_item向量。

items
注意,为了保留API,已将items函数重命名为impl Builder {
    fn generate_items(&self, item: &Item, output: &mut String) -> Vec<Item> {
        match item {
            Item::Direct(v) => {
                output.push_str(v);
                Vec::new()
            }
            Item::Nested(v) => {
                v.iter()
                    .flat_map(|sub_item| self.generate_items(sub_item, output))
                    .collect()
            }
            Item::New(v) => vec![Item::Direct(v.clone())],
        }
    }

    pub fn build_item(&mut self, item: &Item, output: &mut String) {
        let mut new_items = self.generate_items(item, output);
        self.items.append(&mut new_items);
    }

    pub fn build(&mut self, idx: usize, output: &mut String) {
        // Non lexical lifetimes allow this to compile, as the compiler
        // realizes that `item` borrow can be dropped before the mutable borrow

        // Immutable borrow of self starts here
        let item = self.items.get(idx).unwrap();
        let mut new_items = self.generate_items(item, output);
        // Immutable borrow of self ends here

        // Mutable borrow of self starts here
        self.items.append(&mut new_items);
    }
}
,并创建了一个使用build_item的新generate_items函数。

[如果仔细看,您会发现build_item甚至不需要generate_items,并且可以是generate_items中的独立函数或静态函数。

self

© www.soinside.com 2019 - 2024. All rights reserved.