繁体   English   中英

终身省略是否适用于特质的方法?

[英]Does lifetime elision work for methods in trait impls?

有了这个问题,我正在寻找那些在这方面有更多知识的人的反馈。 我绝不是专家。 所以我不妨提前问我的问题:我的推理是否正确?

问题

根据这里关于SO 的问题答案 ,我很困惑地看到在实施特征方法时终生没有了:

impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
    fn eq(&self, other: &RefEquality<T>) -> bool {
        self.0 as *const T == other.0 as *const T
    }
}

这里,在方法签名中,在other类型上省略了生命周期'b 这是有效的,也是正确的。 我期望它是&RefEquality<'b, T> ,因为类型是正确的。 毕竟,这里的'b是必不可少的:生命必须与'a不同。 如果没有,那就太限制了:实现只适用于另一个与Self相同的RefEquality<T> 所以这些显然是不同的语义。 编译器如何推断出正确的生命周期?

终身消费照顾它

可以省略函数签名的生命周期,但不能在impl块上省略它们。 在那里,必须完全指定类型,包括命名生命周期。

另一方面,在eq()方法上,我能够在其他类型注释中忽略生命周期。 实际上,编译器然后为它插入任意生命周期,这明显不同于'a 这就是为什么这样做同时保持相同语义的原因:

impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
    fn eq<'c>(&self, other: &RefEquality<'c, T>) -> bool {
        self.0 as *const T == other.0 as *const T
    }
}

在这里,我为该方法引入了一个任意的生命周期'c ,这与生命周期省略时的编译器基本相同。

在我的特质impl中命名一辈子'b只是声明它必须与'a (我也没有以任何方式链接它们)不同。 从逻辑上讲,这不起作用:

impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
    fn eq(&self, other: &RefEquality<'a, T>) -> bool {
        self.0 as *const T == other.0 as *const T
    }
}

我在impl中说过类型会有所不同(基于它们的生命周期),但现在实际的eq()实现说它们是相同的。 这会导致预期的类型错误。

如果我希望生命时间平等怎么办? 在这种情况下,我仍然可以使用生命周期省略,还是编译器会插入任意生命周期并报告类型错误? 事实证明,推理在这里也能正常工作:

impl<'a, T> PartialEq<RefEquality<'a, T>> for RefEquality<'a, T> {
    fn eq(&self, other: &RefEquality<T>) -> bool {
        self.0 as *const T == other.0 as *const T
    }
}

省略的生命周期将被推断为'a ,保持RefEquality<T>类型必须具有相同生命期望的期望语义。

让我们看看rustc的过程,以确定提供的impl方法是否对应于特征中声明的签名。

在代码中的位置compare_impl_methodlibrustc_typeck/check/compare_method.rs ,它的很好的注释,但连评论都难以用于那些没有编译器的黑客。

我不是编译器开发人员,所以以下是基于我的生锈经验和解释!

特征中的声明对应于特定的函数类型,并且impl块中的定义被解析为其自己的函数类型。

对于这个问题,我认为只有类型检查的结论才是重要的:

  • impl函数是特质函数的子类型吗?”

对我进行分类

如果S是T的子类型,则子类型关系通常写为S <:T,意味着可以在期望类型为T的项的上下文中安全地使用类型S的任何项。

这听起来很合理。 我们希望impl块定义一个可以安全使用的函数,就好像它是在trait中声明的函数一样。

情况1

这是完整的终生案例,但明确说明。 我已经用恐慌替换了所有方法体,以强调函数签名检查完全不受函数体的影响。

impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
    fn eq<'c>(&self, other: &RefEquality<'c, T>) -> bool {
        panic!()
    }
}

该特征需要一个类型的函数:

fn(&RefEquality<'a, T>, &RefEquality<'b, T>)

您提供类型的功能:

fn<'c>(&RefEquality<'a, T>, &RefEquality<'c, T>)

看起来所提供的impl比所需的“更通用”。 使用'c == 'b ,则函数的类型相同。

它是预期类型的​​子类型,因为我们总是可以安全地使用fn<'c>版本。

案例2

对于你的第二个例子,那个没有编译:

impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
    fn eq(&self, other: &RefEquality<'a, T>) -> bool {
        panic!()
    }
}

你可以添加一个绑定的'b: 'a ('b outlives'a),然后就可以了

impl<'a, 'b: 'a, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
    fn eq(&self, other: &RefEquality<'a, T>) -> bool {
        panic!()
    }
}

该特征需要一个类型的函数:

fn(&RefEquality<'a, T>, &RefEquality<'b, T>)

您提供类型的功能:

fn(&RefEquality<'a, T>, &RefEquality<'a, T>)

我认为,如果'b outlives'a,它们是兼容的似乎是合乎逻辑的,但让我们冷静地看待它。

让我们删除常数因素:

该特征需要一个类型的函数:

fn(Ref<'b>)

您提供类型的功能:

fn(Ref<'a>)

我们还有where 'b: 'a 我们怎么能看到它们兼容?

子类型II:方差的攻击

子类型:使用X代替Y是否安全?

方差: 如果 XY的子类型,那么Foo<X>Foo<Y>呢?

另见WikipediaRustonomicon on variance。

生命周期的子类型定义是:

'x <: 'y表示'x长于'y

让我们练习使用引用的子类型和方差。

什么时候使用&'x i32而不是&'y i32是否&'y i32

'x'y更长寿时,那么更换是安全的。 'x生命长于'y意味着&'x i32&'y i32的子类型:

'x <: 'y => &'x i32 <: &'y i32

子类型关系在同一方向传播,这称为协方差 ; &'a i32'a参数中是协变的。

相反,函数的方差行为是这样的:

X <: Y => fn(Y) <: fn(X)

函数的行为方式与其参数类型相反。 这是逆转 ,逻辑上“反对”,因为它是相反的方向。

计算

对于这个问题,我们假设Ref<'a>行为就好像它包含一个&'a引用,并且它&'a本身具有相同的方差

我们被赋予了where 'b: 'a的界限,这意味着:

'b <: 'a

使用协方差规则进行参考和参考:

'b <: 'a => Ref<'b> <: Ref<'a>

对函数使用逆变规则**

Ref<'b> <: Ref<'a> => fn(Ref<'a>) <: fn(Ref<'b>)

这就是rustc问的问题, impl函数是特质函数的一个子类型。 它是!

** wrt函数参数

如果我希望生命时间平等怎么办?

如果你的目标只是为相同的终生案例定义PartialEq ,那么是的, PartialEq lifetime case就可以了。 它在impl中提供了更通用的功能,但类型检查器确定它是兼容的。

您还可以根据生命周期参数更改RefEquality类型的方差。

如果你想要一个RefEquality<'a, T>只是与完全相同的生命周期兼容的子类型,那就叫做不变性

你可以使用一个具有不变性的原语, std::cell::Cell<T> Cell<T>T参数中是不变的。

完成此操作的常用方法是PhantomData成员:

struct RefEquality<'a, T: 'a> {
    ptr: &'a T,
    marker: PhantomData<Cell<&'a ()>>,
}

如果你想看到一个不变量的应用,请查看crossbeam crate以及'a参数中不变量的Scope<'a>其安全范围线程的特殊借用规则的基石。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM