如何在 Rust 中修复“.. 在循环的上一次迭代中可变地借用”?
Posted
技术标签:
【中文标题】如何在 Rust 中修复“.. 在循环的上一次迭代中可变地借用”?【英文标题】:How to fix ".. was mutably borrowed here in the previous iteration of the loop" in Rust? 【发布时间】:2021-05-15 22:55:41 【问题描述】:我必须对键进行迭代,通过键在 HashMap 中找到值,可能在找到的结构中作为值进行一些繁重的计算(惰性 => 改变结构)并将其缓存在 Rust 中返回。
我收到以下错误消息:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:25:26
|
23 | fn it(&mut self) -> Option<&Box<Calculation>>
| - let's call the lifetime of this reference `'1`
24 | for key in vec!["1","2","3"]
25 | let result = self.find(&key.to_owned());
| ^^^^ `*self` was mutably borrowed here in the previous iteration of the loop
...
28 | return result
| ------ returning this value requires that `*self` is borrowed for `'1`
这里是code in playground。
use std::collections::HashMap;
struct Calculation
value: Option<i32>
struct Struct
items: HashMap<String, Box<Calculation>> // cache
impl Struct
fn find(&mut self, key: &String) -> Option<&Box<Calculation>>
None // find, create, and/or calculate items
fn it(&mut self) -> Option<&Box<Calculation>>
for key in vec!["1","2","3"]
let result = self.find(&key.to_owned());
if result.is_some()
return result
None
我无法避免循环,因为我必须检查多个键
我必须让它可变(self
和结构),因为可能的计算会改变它
关于如何改变设计(因为 Rust 迫使以一种有意义的不同方式思考)或解决它的任何建议?
PS。代码还有一些其他的问题,但让我们先拆分问题并解决这个问题。
【问题讨论】:
我已编辑问题以包含完整的错误消息并撤销了我的反对意见。 也相关:Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument? 【参考方案1】:您不能使用 独占 访问权限进行缓存。您不能将 Rust 引用视为通用指针(顺便说一句:&String
和 &Box<T>
是双重间接,并且在 Rust 中非常单一。使用 &str
或 &T
进行临时借用)。
&mut self
不仅意味着可变,而且独占和可变,因此您的缓存支持仅返回一项,因为它返回的引用必须保持self
“锁定”只要它存在。
你需要让借用检查器相信 find
返回的东西不会在你下次调用它时突然消失。目前没有这样的保证,因为接口不会阻止你调用例如items.clear()
(借用检查器检查函数的接口允许什么,而不是函数实际执行什么)。
您可以通过使用Rc
或使用实现a memory pool/arena 的板条箱来做到这一点。
struct Struct
items: HashMap<String, Rc<Calculation>>,
fn find(&mut self, key: &str) -> Rc<Calculation>
这样,如果您克隆 Rc
,它会在需要的时间内存活,与缓存无关。
您还可以通过内部可变性使其更好。
struct Struct
items: RefCell<HashMap<…
这将允许您的记忆 find
方法使用共享借用而不是独占借用:
fn find(&self, key: &str) -> …
这对于方法的调用者来说更容易使用。
【讨论】:
为了修改 Calculation (Rc::get_mut()
) Rc0
,但它可以在某处使用。基本上这会锁定突变,直到它被读取或保存在某个地方。因此,如果我理解正确,仅使用 RC
并没有帮助(而不是编译问题,我们有相同的运行时问题)Rc<RefCell<Calculation>>
。【参考方案2】:
可能不是最干净的方法,但它可以编译。我们的想法是不要将找到的值存储在临时结果中,以避免出现别名:如果存储结果,self
会一直被借用。
impl Struct
fn find(&mut self, key: &String) -> Option<&Box<Calculation>>
None
fn it(&mut self) -> Option<&Box<Calculation>>
for key in vec!["1","2","3"]
if self.find(&key.to_owned()).is_some()
return self.find(&key.to_owned());
None
【讨论】:
这似乎对性能不利:find
被调用了两次。应该有“正道”
我同意你的观点,这可能会更好。另一方面,对于您的具体情况,find
并不昂贵,因为它只是在哈希表中查找,并且您知道该值存在。如果我正确理解您的意图,那么繁重的计算无论如何都会进行一次,所以与此相比,这个双重调用可能非常小。我会试着想一个更好的方法来做到这一点。以上是关于如何在 Rust 中修复“.. 在循环的上一次迭代中可变地借用”?的主要内容,如果未能解决你的问题,请参考以下文章
为什么Rust无法找到wl_display_get_registry?
英伟达 CEO 黄仁勋:摩尔定律结束了;苹果新专利:折叠式iPhone可自行修复折痕;Rust 1.64.0 发布|极客头条