Rust:修改后返回不可变借用

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

在 Rust 中,我尝试更新缓存,然后返回结果。

fn cached_res(&mut self) -> &Vec<u64> {
  if self.last_update.elapsed() >= REFRESH_RATE {
    self.a = do_something(...);
  }
  return &self.a;
}

fn use_cached_res(&mut self) {
  let b = self.cached_res();
  b.iter().map(|x| x + 1).collect()
}

编译失败的原因是

173 |         let b = cached_res(self);
    |                            ---- mutable borrow occurs here
...
179 |                 .map(|f| self.do_something(&f))
    |                  --- ^^^ ---- second borrow occurs due to use of `*self` in closure
    |                  |   |
    |                  |   immutable borrow occurs here
    |                  mutable borrow later used by call

认为我理解为什么它会失败,一个可能的解决方法可能是有一个只更新缓存的方法和另一个返回结果的方法,这样可变借用就会被删除,并且 b 不会被借用为可变。

fn refresh(&mut self) {
  if self.last_update.elapsed() >= REFRESH_RATE {
    self.a = do_something(...);
  }
}
fn res(&self) -> &Vec<u64> {
  &self.a
}
fn use_cached_res(&mut self) {
  self.refresh();
  let b = self.res();
  b.iter().map(|x| x + 1).collect()
}

但是,我希望在

cached_res
中定义这些约束,以便下游用户不必按正确的顺序使用此类方法。 IIUC 我想要的是
cached_res
在修改 self 后返回 self 的不可变借用。

如果我可以提供其他信息或说明,请告诉我。

rust borrow-checker
1个回答
0
投票

解决这个问题的一种方法是使用内部可变性

RefCell
:

use std::cell::RefCell;
use std::ops::Deref;
use std::time::{Duration, Instant};

const REFRESH_RATE: Duration = Duration::from_millis(16);

pub struct A {
    a: RefCell<Vec<u64>>,
    last_update: Instant,
}

impl A {
    fn cached_res(&self) -> impl Deref<Target = Vec<u64>> + '_ {
        if self.last_update.elapsed() >= REFRESH_RATE {
            *self.a.borrow_mut() = Vec::new();
        }
        self.a.borrow()
    }

    pub fn use_cached_res(&self) -> Vec<u64> {
        let b = self.cached_res();
        b.iter().map(|&x| self.process(x)).collect()
    }

    fn process(&self, _x: u64) -> u64 {
        todo!()
    }
}

这会产生一些额外的开销来在运行时跟踪借用,但会给您最大的灵活性,如果您愿意,甚至可以让您返回

impl DerefMut
。如果您需要此结构为
RwLock
,您可以轻松地将其翻译为
borrow_mut
(
write
->
borrow
read
->
Mutex
) 或
lock
(
Sync
)。

另一种方法是通过闭包来反转控制流:

use std::time::{Duration, Instant};

const REFRESH_RATE: Duration = Duration::from_millis(16);

pub struct A {
    a: Vec<u64>,
    last_update: Instant,
}

impl A {
    fn cached_res<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(&Self, &Vec<u64>) -> R,
    {
        if self.last_update.elapsed() >= REFRESH_RATE {
            self.a = Vec::new();
        }
        f(self, &self.a)
    }

    pub fn use_cached_res(&mut self) -> Vec<u64> {
        self.cached_res(|self_, b| b.iter().map(|&x| self_.process(x)).collect())
    }

    fn process(&self, _x: u64) -> u64 {
        todo!()
    }
}

这限制了您如何使用

cached_res
,但不需要更改您的结构或引入开销。

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