Rust 不可变借用,然后以相同的方法改变代码

Rust immutable borrow followed by mutating code in a same method

提问人:Guy 提问时间:8/18/2022 最后编辑:Chayim FriedmanGuy 更新时间:8/18/2022 访问量:42

问:

这是我目前正在构建的 Font 对象的简化版本,用于包含在嵌入式系统中。在 Font 中,我通过 HashMap 实现了已经栅格化的字形的缓存。retrieve() 方法必须首先通过 find() 方法检查缓存中是否已有特定字形。如果未找到,则必须构建字形,将其放入缓存中,然后将其返回给调用方。

问题在于,如果找不到所需的字形,retrieve() 方法必须首先执行不可变借用,然后才能修改缓存。找不到让它正常工作的方法。代码如下:

use std::collections::HashMap;
use std::collections::hash_map::RandomState;

type GlyphCharCode = u32;
type GlyphsCache = HashMap<GlyphCharCode, Glyph, RandomState>;

struct Glyph { a: u8 }

struct Font {
  cache: GlyphsCache
}

impl Font {
  fn new() -> Font {
    Font { cache: GlyphsCache::new() }
  }

  fn find(&self, charcode: GlyphCharCode) -> Option<&Glyph> {
    self.cache.get(&charcode)
  }

  fn retrieve(&mut self, charcode: GlyphCharCode) -> Option<&Glyph> {
    let result = self.find(charcode);
    match result {
      Some(glyph) => Some(glyph),
      None => { self.cache.insert(charcode, Glyph { a: 3u8 });
                self.find(charcode) }
    }
  }
}

fn main() {
  let charcode: GlyphCharCode = 3;
  let mut font = Font::new(); 
  if let Some(glyph) = font.retrieve(charcode) {
    println!("Glyph value: {}", glyph.a);
  }
}

以下是错误消息:

error[E0502]: cannot borrow `self.cache` as mutable because it is also borrowed as immutable
  --> src/main.rs:26:17
   |
22 |   fn retrieve(&mut self, charcode: GlyphCharCode) -> Option<&Glyph> {
   |               - let's call the lifetime of this reference `'1`
23 |     let result = self.find(charcode);
   |                  ------------------- immutable borrow occurs here
24 |     match result {
25 |       Some(glyph) => Some(glyph),
   |                      ----------- returning this value requires that `*self` is borrowed for `'1`
26 |       None => { self.cache.insert(charcode, Glyph { a: 3u8 });
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
不变性 可变

评论


答:

3赞 Silvio Mayolo 8/18/2022 #1

您可以使用 的入口 API 来实现“借用否则”模式。HashMap

fn retrieve(&mut self, charcode: GlyphCharCode) -> &mut Glyph {
  self.cache.entry(charcode).or_insert(Glyph { a: 3u8 })
}

这与您尝试执行的操作完全相同:“如果存在,请返回,或者填写并返回,否则”,但它内置于 .HashMap

您可以考虑使用 or_insert_with,它采用一个闭包,只有在实际需要插入时才会调用该闭包,并且在您的用例中构造成本很高。

fn retrieve(&mut self, charcode: GlyphCharCode) -> &mut Glyph {
  self.cache.entry(charcode).or_insert_with(|| Glyph { a: 3u8 })
}

此外,如果真的那么简单,它应该是,我们可以完全放弃整个生命周期的问题。GlyphCopy

#[derive(Copy)]
struct Glyph { a: u8 }

然后可以按值返回。find

fn find(&self, charcode: GlyphCharCode) -> Option<Glyph> {
  self.cache.get(&charcode).copied()
}

无需生命周期。

评论

0赞 Guy 8/18/2022
感谢西尔维奥的帮助。Glyph 对象比此处显示的要复杂得多,创建它需要使用 freetype 库。我会看看你的建议,并尝试用它做点什么。再次感谢!
0赞 Silvio Mayolo 8/18/2022
@Guy我认为为了我们的利益,它可能会最小化。在这种情况下,该方法应该适合您。您可能还想使用or_insert_with而不是建筑成本高昂(我现在将其编辑到我的答案中)entryor_insert
0赞 Guy 8/18/2022
我刚刚找到它!!谢谢。我将进行一些测试并将其标记为已解决。多谢!!