只有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;
}
评论区
写评论感觉是,"abc" 不像4 或()那样一般不需要分配存储空间,4可以嵌入cpu指令,()甚至不会出现在最后的指令或数据中。"abc"本身就代表一段内存,而且为了效率和重用,一般分配在静态存储区。
从而,如果对4或()引用,也就是取地址,就需要为他们分配一块内存,从而他们就和"abc"一样 可以全局存在。
而如果对 4或() 用let绑定到局部变量,他们就有了局部变量对应的堆栈地址。再对其引用,也就是取地址,就使用的局部变量的地址,而这块内存是随着函数退出,数据失效。所以这些引用life就不能是'static 。
--
👇
苦瓜小仔: 这个说法是不准确的。
"abc" 的类型是 &'static str,它可以存活任意长,直到程序结束。
但
()
并不是引用,它存活任意长是有条件的,所以&()
不一定存活任意长。如果你仔细去看,会发现第一次我写的是:
这次我写的是:
即没有声明
let unit = ();
,&() 变成 &'static () 属于“静态提升”。当然,我也可以都写成
let ref_unit = &(); // &'static ()
这种再去分析:)嗯,细节越来越多 :)
--
👇
leolee0101:
() 跟字符串字面值一样是 'static。
突然发现一个 typo:
应该是:
:(
好的。
之前看 有些生成的 MIR ,就发现有 promoted 的数据结构,跟这儿连上了。
--
👇
苦瓜小仔: 这个论坛也发了。
但两个都不是最新的。
最新的是我给的 github page 上的链接。
这个论坛也发了。
但两个都不是最新的。
最新的是我给的 github page 上的链接。
巧了,刚刚找到你在csdn 里面的那篇 -
--
👇
苦瓜小仔: 前段时间我整理过《临时引用的静态生命周期提升》
前段时间我整理过《临时引用的静态生命周期提升》
长学问了。等会找找 静态提升 的资料。
这个说法是不准确的。
"abc" 的类型是 &'static str,它可以存活任意长,直到程序结束。
但
()
并不是引用,它存活任意长是有条件的,所以&()
不一定存活任意长。如果你仔细去看,会发现第一次我写的是:
这次我写的是:
即没有声明
let unit = ();
,&() 变成 &'static () 属于“静态提升”。当然,我也可以都写成
let ref_unit = &(); // &'static ()
这种再去分析:)嗯,细节越来越多 :)
--
👇
leolee0101:
() 跟字符串字面值一样是 'static。
let ref_unit = &(); // &'static unit -> &'1 unit 协变
之前不知道,() 跟字符串字面值一样是 'static。
这个确实是看漏了,sorry,另外十分感谢。
为什么可以存在 '1 那么长,就结合我第一个回答的第二部分,把 PhantomData 和 InvariantLifetime 类型等价成:
所以和
fn j(c: Cell<&'_ str>)
没什么不同。该讲的都讲了,没什么新鲜的。哎呀,忙中出错,犯低级错误,str 和 string 弄混了。
再次麻烦看下这个 测试
情况一
一个简单的极端情况:'a 为 'static,代码通过,这不难理解,所有引用都保持 'static。
情况二
让 'a 不为 'static, 代码通过。
情况三
refincell 不为 'static,代码通过:
这显示了单独分析
fn j(c: Cell<&'_ str>)
的好处,无需看到'_
在函数外具体是什么生命周期,因为对于函数 j 来说不重要 —— 函数 j 只需要知道'_
是一个外部的、不变的生命周期即可。子类型和型变,赋予的这种“遗忘的能力”。@ 苦瓜小仔 兄,这个例子中 fn j 为什么可以编译?
测试
嗯,感谢耐心解惑!!
从编译结果看:
是的。就是这种情况。
嗯,想讨论的就是“InvariantLifetime<'> 的 ' 这个生命周期”。关键就是认定'l 持续到哪儿,以及何时确定。
“'1 来自于函数体外部,让 &ss 活到 '1,意味着 ss 超出函数体仍然被借用”。在传参时,因为相对'1不变,严格传递'1。那么这时,是编译器已经确定了'1,就是到test_phantom_life调用完成的位置,还是说,此时还是不能确定,还有可能只持续到test_phantom_life内部的某位置。是因为不变,所以在传参时候就必须先确定'1 的确定范围?
--
👇
苦瓜小仔: > 来自函数test_phantom_life外,就认为其life end 也在这个函数外部,尽管其实引用在函数内部调用strictly_match之后就不再使用
是的。对于
fn test_phantom_life(phantom: InvariantLifetime<'_>)
参数 phantom 在函数内结束生命周期 —— 在函数内部调用strictly_match之后结束。
但整个问题讨论的不是 phantom 的生命周期,而是
InvariantLifetime<'_>
的'_
这个生命周期。理解错误消息
phantom 的类型为
InvariantLifetime<'1>
strictly_match(&mut &ss, &phantom);
的参数要求 ss 必须借用到 '1 那么长,即要求 &'1 ss但 &ss 实际活不了 '1 那么长
'1 来自于函数体外部,让 &ss 活到 '1,意味着 ss 超出函数体仍然被借用,而 ss 此时被 drop
最关键的地方在于
^^^
符号指向的地方:即第 3 条。是的。对于
fn test_phantom_life(phantom: InvariantLifetime<'_>)
参数 phantom 在函数内结束生命周期 —— 在函数内部调用strictly_match之后结束。
但整个问题讨论的不是 phantom 的生命周期,而是
InvariantLifetime<'_>
的'_
这个生命周期。理解错误消息
phantom 的类型为
InvariantLifetime<'1>
strictly_match(&mut &ss, &phantom);
的参数要求 ss 必须借用到 '1 那么长,即要求 &'1 ss但 &ss 实际活不了 '1 那么长
'1 来自于函数体外部,让 &ss 活到 '1,意味着 ss 超出函数体仍然被借用,而 ss 此时被 drop
最关键的地方在于
^^^
符号指向的地方:即第 3 条。现在还有点疑问是,报错信息,似乎因为引用来自函数test_phantom_life外,就认为其life end 也在这个函数外部,尽管其实引用在函数内部调用strictly_match之后就不再使用。那么是NLL在跨函数边界,不起作用吗?
不然似乎报错信息应该是 'l strictly outlive ss ,和 strictly_match 的 requirement 冲突。
关于型变,其实我明白你讲的意思。之前不确定的是InvariantLifetime<'_> life 的start 和 end 。是否只看 life end 。嗯,有的地方还得再捋一捋。
--
👇
苦瓜小仔: 在这里,无论是
fn f(phantom: InvariantLifetime<'_>)
还是
fn f(phantom: &InvariantLifetime<'_>)
结果都是一样的。
直觉就是,
'_
是函数外部的引用(或者说生命周期),而 invariance 必须保持生命周期不变,从而不可能被缩短,所以只能比函数内部引用的生命周期更长,永远不可能等于函数内部引用的生命周期。在这里,无论是
fn f(phantom: InvariantLifetime<'_>)
还是
fn f(phantom: &InvariantLifetime<'_>)
结果都是一样的。
直觉就是,
'_
是函数外部的引用(或者说生命周期),而 invariance 必须保持生命周期不变,从而不可能被缩短,所以只能比函数内部引用的生命周期更长,永远不可能等于函数内部引用的生命周期。