我有一个trait Atom
,它有很多相关的类型,其中一个是自有版本OP
,另一个是借用版本O
,本质上是相同的数据,我有一个函数to_pow_view
,它从自有版本创建一个视图,我有一个等式运算符。
下面是一个尝试:
pub trait Atom: PartialEq {
// variants truncated for this example
type P<'a>: Pow<'a, R = Self>;
type OP: OwnedPow<R = Self>;
}
pub trait Pow<'a>: Clone + PartialEq {
type R: Atom;
}
#[derive(Debug, Copy, Clone)]
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum OwnedAtom<R: Atom> {
Pow(R::OP),
}
pub trait OwnedPow {
type R: Atom;
fn some_mutable_fn(&mut self);
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a>;
// compiler said I should add 'a: 'b
fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
if self.to_pow_view().eq(&other) {
self.some_mutable_fn();
}
}
}
impl<R: Atom> OwnedAtom<R> {
// compiler said I should add 'a: 'b, why?
pub fn eq<'a: 'b, 'b>(&'a self, other: AtomView<'b, R>) -> bool {
let a: AtomView<'_, R> = match self {
OwnedAtom::Pow(p) => {
let pp = p.to_pow_view();
AtomView::Pow(pp)
}
};
match (&a, &other) {
(AtomView::Pow(l0), AtomView::Pow(r0)) => l0 == r0,
}
}
}
// implementation
#[derive(Debug, Copy, Clone, PartialEq)]
struct Rep {}
impl Atom for Rep {
type P<'a> = PowD<'a>;
type OP = OwnedPowD;
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct PowD<'a> {
data: &'a [u8],
}
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
}
struct OwnedPowD {
data: Vec<u8>,
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn some_mutable_fn(&mut self) {
todo!()
}
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a> {
PowD { data: &self.data }
}
}
fn main() {}
此代码给出错误:
27 | fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
| -- lifetime `'b` defined here
28 | if self.to_pow_view().eq(&other) {
| ------------------
| |
| immutable borrow occurs here
| argument requires that `*self` is borrowed for `'b`
29 | self.some_mutable_fn();
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
我希望这能起作用,因为在eq
函数求值之后,应该立即删除不可变的借位。
在这段代码中,生存期的设置有错误,已经在等式函数eq
中:我期望'a
和'b
之间没有关系;他们应该活得足够长来做比较。但是,编译器告诉我应该添加'a: 'b
,我不明白为什么。同样的事情发生在函数test
上。
这些问题使我相信to_pow_view
中的生存期是错误的,但是我尝试的任何修改都没有使它工作(除了删除&'a self
上的'a
生存期,但是OwnedPowD
不再编译)。
Link to playground
有人能帮我弄清楚到底发生了什么吗?
1条答案
按热度按时间omhiaaxx1#
重点是:您将
Pow
约束为PartialEq
。但是,PartialEq
为PartialEq<Self>
。换句话说,**Pow<'a>
仅为相同的'a
**实现PartialEq<Pow<'a>>
。对于任何具有生存期和
PartialEq
的类型通常都是这种情况,那么为什么它总是工作,但在这里不工作呢?这通常是有效的,因为如果我们比较
T<'a> == T<'b>
,编译器可以将生存期缩短到两者中最短的,然后进行比较。然而,
Pow
是一个trait。trait在它们的生命周期内是 * 不变的 *,换句话说,它必须保持原样,不能更长也不能更短。这是因为它们可能与不变类型一起使用,例如Cell<&'a i32>
。下面是一个例子,说明如果允许这样做,它将如何被利用:上面的代码不能编译,因为
Evil
相对于'a
是不变的,但是如果它是不变的,那么它在安全代码中会包含UB,因此,可能包含Evil
类型的trait在它们的生命周期中也是不变的。因此,编译器不能缩短
other
的生存期,但可以缩短self.to_pow_view()
的生存期(在test()
中,eq()
类似),因为它并没有真正收缩它,只是为to_pow_view()
的self
选取了一个较短的生存期,但是因为PartialEq
只对具有相同生存期的类型实现,这意味着从self.to_pow_view()
得到的Pow
必须具有与other
相同的寿命。因此,(a)'a
必须大于或等于'b
,所以我们可以从中挑选出'b
,并且(B)通过比较,我们借用self
来代替潜在的整个'a
,因为它可能是那个'a == 'b
,因此比较借用self
来代替'a
,所以当我们对some_mutable_fn()
可变地借用它时,它仍然是不可变地借用的。一旦我们理解了这个问题,我们就可以考虑解决方案了,要么我们要求
Pow
在'a
上协变(可以收缩),要么我们要求它在任何生存期'b
上实现PartialEq<Pow<'b>>
,第一个在Rust中是不可能的,但第二个是可能的:这将触发错误,因为自动派生的
PartialEq
不满足此要求:所以我们需要手动实现
PartialEq
:现在它起作用了。