【发布时间】:2020-06-14 13:52:04
【问题描述】:
如何根据来自Vec 中的项目的信息修改Vec,而不具有对向量的不可变和可变引用?
我试图创建一个最小的示例来演示我的具体问题。在我的真实代码中,Builder 结构已经是其他答案提出的中间结构。具体来说,我认为其他问题无法回答这个问题,因为:
-
Why does refactoring by extracting a method trigger a borrow checker error? - 我会在中间结构中放入什么?我没有计算来自
Vec<Item>的单独值。 上正在修改/操作的值是Vec<Item>并且是中间结构中需要的值
假设我有一个项目定义列表,其中项目是一个字符串、Items 的嵌套列表,或者表明应该将一个新项目添加到正在处理的项目列表中:
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 结构能够根据其中一项中包含的信息(即不可变借用self.items)。
这是代码的playground example。
使用Clone
@Stargateur 建议我尝试克隆 build() 中的项目。虽然这确实有效,但出于性能原因,我一直在尝试不克隆项目。 更新:在没有使用Item::New 添加Vec<Item> 修改功能的情况下,我在我的真实代码中实现了clone() 方法,并克隆了与上面示例build() 方法等效的值。当我使用self.items.get(idx).unwrap().clone() 与self.items.get(idx).unwrap() 时,我看到性能下降了 12 倍。我将继续寻找其他解决方案。问题是,我对 Rust 还比较陌生,即使使用不安全的代码也不知道如何改变规则/做其他事情。
有效的代码 (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()),
}
}
}
并更改 build 以首先克隆该项目:
let item = self.items.get(idx).unwrap().clone();
【问题讨论】:
-
嗯,它在某些方面很相似,但我想我最终会遇到同样的错误,创建一个中间结构 - 我仍然必须引用
Vec<Item>中的一个项目才能构造中间结构。有趣的是,在我的真实代码中Builderis 已经是中间结构 -
哦,是的,您的情况也有问题,因为您不能将向量借用为不可变的,并且他们对其进行了修改。您需要克隆项目以避免借用,或者您需要处理
build中的匹配项,因为编译器无法知道修改向量时借用可能会结束 (play.rust-lang.org/…)。 -
我会尝试克隆值 - 出于性能原因,我一直在努力避免克隆。
-
@Stargateur 我已经更新了我的问题以包含一个潜在的解决方案,该解决方案为
Item实现了Clone特征。这确实有效,但我不确定我的实际项目会对性能造成多大的影响。如果我没有提出任何其他建议,我会将其标记为答案(如果您将其设为单独的答案)。 -
您能否提供更多背景信息,以解释您真正想要做什么?因为可能有更好的方法来构建数据以获得更好的性能,而不会遇到借位检查错误。例如,一种可能性是使用竞技场而不是
Vec,但如果没有更多上下文,很难知道这是否适合您。这里要认识到的一件重要的事情是,Rust 不会因为编译器错误而令人讨厌:它可以保护您免受实际内存损坏,当 Vec 重新分配并使您的 &Item 悬空时会发生这种情况。
标签: vector rust borrow-checker