我有一个特征Atom,它有许多关联类型,其中一个是拥有的版本OP,另一个是本质上相同的数据的borrow 版本O.我具有从所拥有的版本创建视图的函数to_pow_view,并且我具有相等运算符.

以下是一次try :

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的生存期是错误的,但我try 的任何修改都没有使其工作(除了删除&'a self'a生存期,但随后OwnedPowD不再编译).

Link to playground

有人能帮我了解一下发生了什么事吗?

推荐答案

关键是:你把Pow限制为PartialEq.然而,PartialEq等于PartialEq<Self>.换句话说,104 only implements 105 for the same 106.

对于任何具有生存期和PartialEq的类型,通常都是这种情况,那么为什么它总是有效,但在这里不起作用呢?

它通常是有效的,因为如果我们比较T<'a> == T<'b>,编译器可以将生命周期缩短到两者中最短的一个,然后进行比较.

然而,Pow是一种特征.在他们的一生中,特征是invariant,换句话说,它必须完全保持原样,不能更长也不能更短.这是因为它们可以与不变类型一起使用,例如Cell<&'a i32>.以下是一个示例,说明如果允许这样做,它将如何被利用:

use std::cell::Cell;

struct Evil<'a> {
    v: Cell<&'a i32>,
}

impl PartialEq for Evil<'_> {
    fn eq(&self, other: &Self) -> bool {
        // We asserted the lifetimes are the same, so we can do that.
        self.v.set(other.v.get());
        false
    }
}

fn main() {
    let foo = Evil { v: Cell::new(&123) };
    {
        let has_short_lifetime = 456;
        _ = foo == Evil { v: Cell::new(&has_short_lifetime) };
    }
    // Now `foo` contains a dangling reference to `has_short_lifetime`!
    dbg!(foo.v.get());
}

上面的代码没有编译,因为Evil'a上是不变的,但如果编译,它将在安全代码中包含UB.因此,可能包含Evil等类型的特征在其生命周期中也是不变的.

正因为如此,编译器无法缩短other的生命周期.它可以缩短self.to_pow_view()的生命周期 (在test()中,eq()是相似的),因为它并没有真正地缩短它,它只是 Select 了to_pow_view()self的较短的生命周期 .但由于PartialEq只针对具有相同生存期的类型实现,这意味着从self.to_pow_view()产生的Pow必须具有相同的生存期other.正因为如此,(A)'a必须大于或等于'b,所以我们可以从中 Select 'b,以及(B)通过比较,我们借入self可能是整个'a,因为它可能是'a == 'b,因此比较借入self'a,所以它仍然是固定借入的,而我们不变地借入some_mutable_fn().

一旦我们了解了问题,我们就可以考虑解决方案.要么我们要求Pow'a上协变(可以缩小),要么我们要求它在任何生命周期'b中实现PartialEq<Pow<'b>>.第一个在铁 rust 中是不可能的,但第二个是可能的:

pub trait Pow<'a>: Clone + for<'b> PartialEq<<Self::R as Atom>::P<'b>> {
    type R: Atom;
}

这会触发错误,因为自动派生的PartialEq不满足此要求:

error: implementation of `PartialEq` is not general enough
  --> src/main.rs:73:10
   |
73 | impl<'a> Pow<'a> for PowD<'a> {
   |          ^^^^^^^ implementation of `PartialEq` is not general enough
   |
   = note: `PartialEq<PowD<'0>>` would have to be implemented for the type `PowD<'a>`, for any lifetime `'0`...
   = note: ...but `PartialEq` is actually implemented for the type `PowD<'1>`, for some specific lifetime `'1`

因此,我们需要手动实现PartialEq个:

impl<'a, 'b> PartialEq<PowD<'b>> for PowD<'a> {
    fn eq(&self, other: &PowD<'b>) -> bool {
        self.data == other.data
    }
}

现在它起作用了.

Rust相关问答推荐

有没有更好的方法从HashMap的条目初始化 struct ?

有没有办法避免在While循环中多次borrow `*分支`

原始数组数据类型的默认trait实现

使用铁 rust S还原对多个数组执行顺序kronecker积

为什么这是&q;,而让&q;循环是无限循环?

如何模拟/创建ReqData以测试Actix Web请求处理程序?

告诉Rust编译器返回值不包含构造函数中提供的引用

有没有一种惯用的方法来判断VEC中是否存在变体?

当发送方分配给静态时,Tokio MPSC关闭通道

如何在嵌套的泛型 struct 中调用泛型方法?

如何初始化选项<;T>;数组Rust 了?

如何返回 struct 体中向量的切片

为什么 js_sys Promise::new 需要 FnMut?

为什么是&mut发送?线程如何在安全的 Rust 中捕获 &mut?

为什么需要同时为值和引用实现`From`?方法不应该自动解引用或borrow 吗?(2023-06-16)

Rust 引用元组和引用元组

Rust 中 `Option` 的内存开销不是常量

将文件的第一行分别读取到文件的其余部分的最有效方法是什么?

如何为返回正确类型的枚举实现 get 方法?

使用 rust-sqlx/tokio 时如何取消长时间运行的查询