如何允许不可变和可变借用共存与显式生命周期的函数?



对于上下文:我正在实现一个实体,基本上是围绕异构映射的薄包装,并试图实现一个update方法:

struct Entity {
pub id: u64,
data: HashMap<TypeId, Box<dyn Any>>,
}
impl Entity {
pub fn new(id: u64) -> Self {
Entity { id, data: HashMap::new() }
}
pub fn get_atom<T: Any>(&self) -> Option<&T> {
self.data.get(&TypeId::of::<T>())?.downcast_ref()
}
pub fn set_atom<T: Any>(&mut self, value: T) {
self.data.insert(TypeId::of::<T>(), Box::new(value));
}
pub fn update<T: Any, R: Any>(&mut self, f: impl Fn(&T) -> R) 
{
if let Some(val) = self.get_atom() {
self.set_atom(f(val));
}
}
}

这个可以工作,但是给出一个警告:

warning: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/entity.rs:71:13
|
70 |         if let Some(val) = self.get_atom() {
|                            --------------- immutable borrow occurs here
71 |             self.set_atom(f(val));
|             ^^^^^^^^^^^^^^^^---^^
|             |               |
|             |               immutable borrow later used here
|             mutable borrow occurs here

好的,这是可以修复的,这样就删除了警告。

pub fn update<T: Any, R: Any>(&mut self, f: impl Fn(&T) -> R) 
{
let maybe : Option<R> = self.get_atom().map(f);
if let Some(val) = maybe {
self.set_atom(val);
}
}

我遇到的问题是,当我引入一个trait来抽象从实体中获取的东西时(这允许我抽象实体:如果每个组件都在实体中,则获得一个元组返回一个值):

trait GetComponent<'a, T> {
fn get_component(entity: &'a Entity) -> Option<T>;
}
// sample atom impl
impl <'a> GetComponent<'a, &'a u32> for &'a u32 {
fn get_component(entity: &'a Entity) -> Option<&'a u32> {
entity.get_atom()
}
}
// sample composite impl
impl <'a, A, B> GetComponent<'a, (A, B)> for (A, B) where 
A: GetComponent<'a, A>, 
B: GetComponent<'a, B>,
{
fn get_component(entity: &'a Entity) -> Option<(A, B)> {
Some((A::get_component(entity)?, B::get_component(entity)?))
}
}
impl Entity {
<in addition to the above>
pub fn get<'a, T: GetComponent<'a, T>>(&'a self) -> Option<T> {
T::get_component(self)
}
pub fn update<'a, T: 'a, R: Any>(&'a mut self, f: impl Fn(&T) -> R) 
where &'a T: GetComponent<'a, &'a T>
{
let maybe : Option<R> = self.get().map(f);
if let Some(val) = maybe {
self.set_atom(val);
}
}
}

这个特性要求我明确生命周期,到目前为止,我对这个实体所做的一切似乎都很好。但是这个更新方法给出了以下编译错误(这次不是警告):

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/entity.rs:56:13
|
51 |     pub fn update<'a, T: 'a, R: Any>(&'a mut self, f: impl Fn(&T) -> R) 
|                   -- lifetime `'a` defined here
...
54 |         let maybe : Option<R> = self.get().map(f);
|                                 ----------
|                                 |
|                                 immutable borrow occurs here
|                                 argument requires that `*self` is borrowed for `'a`
55 |         if let Some(val) = maybe {
56 |             self.set_atom(val);
|             ^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

现在,据我所知,没有理由不能在较短的时间内不可变借用,所以不可变借用"到期"了;就像在性状之前没有显式生命周期的版本一样。

但是我无论如何也想不出该怎么做。

你可以用更高等级的特征界(HRTB)来解决这个问题:

pub fn update<T, R: Any>(&mut self, f: impl Fn(&T) -> R) 
where for <'a> &'a T: GetComponent<'a, &'a T>
这说明对于任何可能的寿命'a,T必须满足给定的界。这允许您指定类型T上的各种生命周期如何相互关联,而无需将它们绑定到特定的生命周期,例如&mut self

。 (游乐场)

最新更新