我感到愚蠢的是,浏览了marker section Rust文档和维基百科关于subtyping和variance多次的文章而没有提高我对生命周期子类型关系的理解。
我认为我只是习惯于“典型的OOP风格”子类关系,例如“Cat&lt ;: Animal”,意思是“Cat是动物的子类型”,其中“S是T的子类型”意味着“任何术语” S可以安全地用于期望类型为T的术语“。到目前为止一切都很好。
但这如何适用于生命?现在在Rust中定义的方式显然是(*)
(#1)'a<:'b< =>寿命a不等于寿命b。
你可能会想“当然这意味着什么!”可能是因为<:看起来类似于小于运算符,或者可能是因为“sub”让你想到子集,更短的寿命肯定是更长寿命的一个子集。但是'如果'a'不是'b',那么'b'真的是一个子类型让我们尝试应用维基百科对子类型关系的定义:
(#2)'a<:'b< =>生命周期a可以安全地用于预期寿命为b的环境中。
我遇到的问题是我无法调和此问题。你是如何从#2到#1的?因为对我而言,这似乎是一个矛盾...如果你期望某事至少是活着的b并且你有一个比b短的东西,你显然不能在那些有生命周期的东西中使用它是必需的,可以吗?它只是我还是我们得到了生命周期的子类型关系错误?
编辑:(*)根据#rust
IRC频道中的Ms2ger,情况就是如此。它也适用于Items
迭代器中使用的逆变寿命标记的文档。
Edit2:已删除ContravariantLifetime和CovariantLifetime标记。我们现在PhantomData
模块中有{{1}}作为替代。
答案 0 :(得分:2)
免责声明:我不是一个CS大师,所以这个答案将集中在实践概念上,我甚至不会尝试将其与理论概念联系起来,以免弄得一团糟。
我认为问题是尝试将子类型概念应用于非类型的东西。
'a
是一生&'a T
是一种类型您可以比较&'a T
和&'b U
并查看它们是否服从子类型关系,但是您无法在摘要中建立两个生命周期的子类型关系,因为:
我们可以通过两个简单的例子来检查这一点。
第一个例子可能是最简单的:如果寿命更长,可以替换生命周期!
// Using a lifetime as a bound
struct Reference<'a, T>
where T: 'a
{
data: &'a T
}
fn switch<'a, 'b, T>(r: &mut Reference<'a, T>, new: &'b T)
where 'b: 'a
{
r.data = new;
}
这里,编译器只允许替换,如果'b
至少与'a
一样长,它由生命周期绑定'b: 'a
表示。这是因为Rust憎恨悬空引用,因此容器只能包含对比它更长的对象的引用。
当用作保证时,更长的生命周期是较短生命周期的子类型,可以替代它。这提示@aturon提到,在这种用法中'static
是所有生命周期的子类型。
第二个例子有点棘手:如果生命周期较小,可以取代生命周期!
让我们从以下开始:
struct Token;
fn restrict<'a, 'b, T>(original: &'a T, _: &'b Token) -> &'b T
where 'a: 'b
{
original
}
以下用法是正确的:
fn main() {
let i = 4;
{
let lesser = Token;
let k = restrict(&i, &lesser);
println!("{}", k);
}
}
我们之前的演示说我们可以用更长的寿命代替较小的寿命:
fn main() {
let greater = Token;
let j; // prevent unification of lifetimes
{
let i = 4;
j = restrict(&i, &greater);
}
println!("{}", j);
}
error: `i` does not live long enough
j = restrict(&i, &greater);
当用作约束时,较短的生命周期是更长寿命的子类型,可以替代它。在这种用法中,'static
是所有生命周期的超类型。
因此,生命之间没有单一的子类型关系,因为它们有两个截然相反的目的!
概括说明:
greater <: lesser
lesser <: greater
注意:有些生命周期可以同时作为保证和约束。