只有phantom 的结构,携带一个虚拟的引用,这个引用的lifetime是多少。
struct InvariantLifetime<'id>(PhantomData<*mut &'id ()>);
上面链接中,几个例子中 InvariantLifetime 的lifetime 应该怎么推断?
======
新增:
似乎像如下使用,InvariantLifetime也像普通引用一样,lifetime 至少到 最后一次使用。
但是被传参到函数内,似乎不是?
use std::marker::PhantomData;
//#[derive(Clone, Copy, Default)]
struct InvariantLifetime<'id>(PhantomData<*mut &'id ()>);
fn main() {
let t :InvariantLifetime = InvariantLifetime(PhantomData);
fn strictly_match<'a>(_: &mut &'a String, _: &InvariantLifetime<'a>)
{}
{
let ss = String::from("asd");
strictly_match(&mut &ss, &t);
drop (ss);
}
//@ 和普通引用一样,如果这里使用,会报错:t的life超过了ss
t;
}
评论区
写评论我知道我的哪句话误导你了。
嗯,我的表述这里不对。没有歧义的话是:
我指的“&ss 无法和它那样一直存在”,就是 '2 无法和 '1 那样存在时间(跨度)同样长。
至于 “因为 ss 离开函数体不存在,所以 &ss 不存在”,这句话是对的,但 InvariantLifetime 同样无法离开函数体。
写的时候我可能想成了
fn f(phantom: &InvariantLifetime<'_>)
。这句话也没错。因为生命周期关注的是,在哪里结束,从而不会出现引用失效。但这句话不是关键。
'1 与 '2 相等只有两种途径:
f(&'2 str, &'tmp Cell<&'2 str>)
:这不行,因为 invariance, '1 必须保持不变,无法进行任何变化f(&'1 str, &'tmp Cell<&'1 str>)
:也不行,由于 '1 大于 '2,无法把 '2 变得更长, &'2 str 要么保持 '2,要么通过 & 的协变,变成 '3(当然 '2: '3),即一个更短的生命周期这个我马上看下。但您觉得他说的"Note that lifetimes only restrict the end of life of things. "是什么意思,而且看了回答,从头到尾都是说的 life 到哪里end ,从没说从哪儿 start 。
👇
苦瓜小仔: 我知道,那是我提问的。
嗯,从型变看,的确没什么太大的区别。
但你这次问的,类似于:
g
无法编译。我知道,那是我提问的。
嗯,从型变看,的确没什么太大的区别。
但你这次问的,类似于:
g
无法编译。在上次有个问题里,您帮我在官方论坛里发起的提问中。有大佬回答的"Note that lifetimes only restrict the end of life of things. "
https://users.rust-lang.org/t/help-me-to-understand-invariance-and-lifetime-here/77835 .
--
👇
苦瓜小仔: > lifetime 约束是否只是看life end 的地方?
不是。如果只看 lifetime 实际在哪结束,为什么需要函数体的生命周期标注呢?
是的。但你依然没看到 'tmp2 无法与 'outer 相等:'outer 永远大于 'tmp2。
不是。如果只看 lifetime 实际在哪结束,为什么需要函数体的生命周期标注呢?
是的。但你依然没看到 'tmp2 无法与 'outer 相等:'outer 永远大于 'tmp2。
我也迷糊了。先达成一个观点:lifetime 约束是否只是看life end 的地方?如果是,'tmp2 和 'outer 都是止步于 strictly_match 调用之后。
--
👇
苦瓜小仔: ```rust fn test_phantom_life<'outer>(phantom: InvariantLifetime<'outer>) { let ss = String::from("asd"); let ref_phantom = &phantom; // &'tmp1 InvariantLifetime<'outer> let ref_ss = &ss; // &'tmp2 String strictly_match(&mut ref_ss, ref_phantom); // 'tmp2 无法等于 'outer // NLL: phantom 和 ss 的生命周期的确止步于此,但这不是关键所在 }
InvariantLifetime<'outer> 在函数内部不会改变 'outer,因为这就是 invariance。
lifetime约束是不是只看life end 的位置?进入函数之前 InvariantLifetime<'_> 的life end 是未知的,到了最后使用的地方strictly_match(&mut &ss, &phantom) &ss 和 sInvariantLifetime<'_> 都是最后一次使用,life end 不是一样吗
--
👇
苦瓜小仔: 'inner 和 'outer 一样在协变情况下是可以发生。
但你这里是不变啊。
NLL 的确让变量的生命周期终止于最后使用的时候,但 strictly_match(&mut &ss, &phantom); 这行显然还在使用中,所以哪里有问题?
'inner 和 'outer 一样在协变情况下是可以发生。
但你这里是不变啊。
NLL 的确让变量的生命周期终止于最后使用的时候,但 strictly_match(&mut &ss, &phantom); 这行显然还在使用中,所以哪里有问题?
lifetime 约束不是只约束结束的位置吗?"Note that lifetimes only restrict the end of life of things. "https://users.rust-lang.org/t/help-me-to-understand-invariance-and-lifetime-here/77835 .
--
👇
苦瓜小仔:
在你标注的这种不变的情况下,
'inner
怎么可能和 'outer 一样?--
👇
leolee0101: InvariantLifetime<'_>,在传入函数后,除了strictly_match(&mut &ss, &phantom),之后ss 和 InvariantLifetime<'_> 都不再使用。 test 函数返回之后,两者也不会使用。根据NLL,他们的life 不是只到最后一次使用的地方吗?
在你标注的这种不变的情况下,
'inner
怎么可能和 'outer 一样?--
👇
leolee0101: InvariantLifetime<'_>,在传入函数后,除了strictly_match(&mut &ss, &phantom),之后ss 和 InvariantLifetime<'_> 都不再使用。 test 函数返回之后,两者也不会使用。根据NLL,他们的life 不是只到最后一次使用的地方吗?
@ 苦瓜小仔 兄,“ss 来自于函数内部,其引用只在函数内部有效,所以 InvariantLifetime<'_> 能一直存在,但 &ss 无法和它那样一直存在”。InvariantLifetime<'_>,在传入函数后,除了strictly_match(&mut &ss, &phantom),之后ss 和 InvariantLifetime<'_> 都不再使用。 test 函数返回之后,两者也不会使用。根据NLL,他们的life 不是只到最后一次使用的地方吗?
先回答为什么不能编译
这需要关注函数签名中生命周期标注的含义。
fn strictly_match<'a>(_: &mut &'a String, _: &InvariantLifetime<'a>)
这个函数所表明的生命周期“契约”之一是:&'a String
一直存在,InvariantLifetime<'a>
就必须一直存在;InvariantLifetime<'a>
一直存在,&'a String
就必须一直存在。好了,这句话可以解释除这个函数之外,你列举的函数为什么无法编译。
ss 来自于函数内部,其引用只在函数内部有效,所以
InvariantLifetime<'_>
能一直存在,但 &ss 无法和它那样一直存在(因为 ss 离开函数体不存在,所以 &ss 不存在)。同理:
虽然 ss 来自外部,但其引用产生于函数内部,所以无法编译。同理,另外两个只是参数使用的模式不一样之外,与这个函数并无区别。
再思考为什么能编译的部分
把关注点放到 InvariantLifetime 上,一个最基础的问题,变量 t 是什么类型?
如果你试图去理解生命周期,那么这是必须清楚的,而不是只看到你所标注的
InvariantLifetime
,却遗忘它的生命周期参数。它是一个被推断的生命周期,其类型为 InvariantLifetime<'_>,而且不是 InvariantLifetime<'static>。
给这个推断的生命周期一个名称
'pass
,那么 t 的类型是InvariantLifetime<'pass>
。只要能“证明”确实可以有
&'pass ss
与InvariantLifetime<'pass>
,那么编译通过就不奇怪,对吧?PhantomData
确实是一个非常复杂的东西,当你把它具体化:一切都可以讲清楚了:
let phantom: PhantomData<'_> = PhantomData(&mut &() as _);
其实与如下内容等价:&unit
对生命周期是协变的,在let phantom_inner: *mut &'_ () = &mut &() as _;
之前可以缩短成let phantom_inner: *mut &'pass () = &mut &() as _;
。同理,
&ss
对生命周期是也是协变的,strictly_match(&mut &ss, &t);
等价于:&'pass ss
与InvariantLifetime<'pass>
都存在,所以代码通过!这种标注的问题
fn strictly_match<'a>(_: &mut &'a String, _: &InvariantLifetime<'a>)
这种标注当然是完全不推荐,它相当无用。这至少会带来一个问题:当引用
&'a String
不再存在时,InvariantLifetime<'a>
不能再使用(如果使用,就是违反生命周期契约,导致编译失败):解决办法
fn strictly_match(_: &mut &String, _: &InvariantLifetime)
这相当简洁,而且生命周期约束很宽松:
&String
与InvariantLifetime<'_>
没有任何关系,各自的生命周期互不影响,所以也就没有我提到的问题。