- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在偶然发现下面的代码之前,我深信类型的生命周期参数中的生命周期将永远超过其自身的实例。换句话说,给定foo: Foo<'a>
,那么'a
将永远超过foo
。然后,@ Luc Danton(Playground)向我介绍了此反参数代码:
#[derive(Debug)]
struct Foo<'a>(std::marker::PhantomData<fn(&'a ())>);
fn hint<'a, Arg>(_: &'a Arg) -> Foo<'a> {
Foo(std::marker::PhantomData)
}
fn check<'a>(_: &Foo<'a>, _: &'a ()) {}
fn main() {
let outlived = ();
let foo;
{
let shortlived = ();
foo = hint(&shortlived);
// error: `shortlived` does not live long enough
//check(&foo, &shortlived);
}
check(&foo, &outlived);
}
foo
创建的
hint
似乎考虑了生命周期不长于其自身的生存期,并且对其的引用被传递给了范围更广的函数,但代码仍按原样进行编译。取消注释代码中指出的行会触发编译错误。另外,将
Foo
更改为结构元组
(PhantomData<&'a ()>)
也会使代码不再以相同类型的错误(
Playground)进行编译。
最佳答案
尽管您的意图最好,但是hint
函数可能没有达到您期望的效果。但是,在我们了解发生了什么之前,我们有很多基础要覆盖。
让我们从这个开始:
fn ensure_equal<'z>(a: &'z (), b: &'z ()) {}
fn main() {
let a = ();
let b = ();
ensure_equal(&a, &b);
}
main
中,我们定义了两个变量
a
和
b
。由于它们是由不同的
let
语句引入的,因此它们具有不同的生存期。
ensure_equal
需要两个具有相同生存期的引用。但是,此代码可以编译。为什么?
'a: 'b
(阅读:
'a
超过
'b
),
&'a T
是
&'b T
的
subtype。
a
的生存期为
'a
,而
b
的生存期为
'b
。这是
'a: 'b
的事实,因为首先引入了
a
。在调用
ensure_equal
时,分别将参数键入
&'a ()
和
&'b ()
1。这里存在类型不匹配的情况,因为
'a
和
'b
的生存期不同。但是编译器还没有放弃!它知道
&'a ()
是
&'b ()
的子类型。换句话说,
&'a ()
是
&'b ()
。因此,编译器将强制表达式
&a
键入
&'b ()
,以便两个参数都键入
&'b ()
。这样可以解决类型不匹配的问题。
&'a ()
替换
Programmer
,用
&'b ()
替换
Person
。现在让我们说
Programmer
是从
Person
派生的:
Programmer
因此是
Person
的子类型。这意味着我们可以采用
Programmer
类型的变量,并将其作为参数传递给需要
Person
类型的参数的函数。这就是以下代码将成功编译的原因:编译器会将
T
解析为
Person
,以用于
main
中的调用。
class Person {}
class Programmer extends Person {}
class Main {
private static <T> void ensureSameType(T a, T b) {}
public static void main(String[] args) {
Programmer a = null;
Person b = null;
ensureSameType(a, b);
}
}
Programmer
是
Person
是安全的,但是您不能假定
Person
是
Programmer
。同样,可以安全地假设变量的生存期较短,但是您不能假设某个已知生存期的变量实际上具有更长的生存期。毕竟,Rust的整个生命周期都是为了确保您不能访问超出其实际生命周期的对象。
Variance is a property that type constructors have with respect to their arguments. A type constructor in Rust is a generic type with unbound arguments. For instance
Vec
is a type constructor that takes aT
and returns aVec<T>
.&
and&mut
are type constructors that take two inputs: a lifetime, and a type to point to.
Vec<T>
的所有元素都具有相同的类型(并且我们此处不讨论特征对象)。但是差异让我们以此作弊。
&'a T
是
'a
和
T
的协变变量。这意味着,只要我们在类型参数中看到
&'a T
,就可以用
&'a T
的子类型替换它。让我们看看它是如何工作的:
fn main() {
let a = ();
let b = ();
let v = vec![&a, &b];
}
a
和
b
具有不同的生存期,并且表达式
&a
和
&b
不具有相同的type1。那么为什么我们可以用这些来制作一个
Vec
呢?推理与上述相同,因此我将总结一下:
&a
强制为
&'b ()
,因此
v
的类型为
Vec<&'b ()>
。
fn(T)
是Rust中的一种特殊情况。
fn(T)
与
T
是相反的。让我们构建一个函数的
Vec
!
fn foo(_: &'static ()) {}
fn bar<'a>(_: &'a ()) {}
fn quux<'a>() {
let v = vec![
foo as fn(&'static ()),
bar as fn(&'a ()),
];
}
fn main() {
quux();
}
v
中的
quux
是什么类型?是
Vec<fn(&'static ())>
还是
Vec<fn(&'a ())>
?
fn foo(_: &'static ()) {}
fn bar<'a>(_: &'a ()) {}
fn quux<'a>(a: &'a ()) {
let v = vec![
foo as fn(&'static ()),
bar as fn(&'a ()),
];
v[0](a);
}
fn main() {
quux(&());
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> <anon>:5:13
|
5 | let v = vec![
| _____________^ starting here...
6 | | foo as fn(&'static ()),
7 | | bar as fn(&'a ()),
8 | | ];
| |_____^ ...ending here
|
note: first, the lifetime cannot outlive the lifetime 'a as defined on the body at 4:23...
--> <anon>:4:24
|
4 | fn quux<'a>(a: &'a ()) {
| ________________________^ starting here...
5 | | let v = vec![
6 | | foo as fn(&'static ()),
7 | | bar as fn(&'a ()),
8 | | ];
9 | | v[0](a);
10| | }
| |_^ ...ending here
note: ...so that reference does not outlive borrowed content
--> <anon>:9:10
|
9 | v[0](a);
| ^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that types are compatible (expected fn(&()), found fn(&'static ()))
--> <anon>:5:13
|
5 | let v = vec![
| _____________^ starting here...
6 | | foo as fn(&'static ()),
7 | | bar as fn(&'a ()),
8 | | ];
| |_____^ ...ending here
= note: this error originates in a macro outside of the current crate
error: aborting due to previous error
&'a ()
参数调用向量中的函数之一。但是
v[0]
需要一个
&'static ()
,并且不能保证
'a
是
'static
,因此这是无效的。因此,我们可以得出结论
v
的类型是
Vec<fn(&'static ())>
。如您所见,协方差与协方差相反:我们可以用更长的生命周期代替较短的生命周期。
hint
调用中产生了什么。
hint
具有以下签名:
fn hint<'a, Arg>(_: &'a Arg) -> Foo<'a>
Foo
与
'a
是相反的,因为
Foo
包裹了
fn
(或者,由于
PhantomData
,所以假装为,但是当我们谈论方差时并没有区别;两者具有相同的作用),
fn(T)
与
T
是相反的,并且
T
在这里是
&'a ()
。
hint
的调用时,它仅考虑
shortlived
的生存期。因此,
hint
返回具有
Foo
生命周期的
shortlived
。但是,当我们尝试将其分配给变量
foo
时,我们遇到了一个问题:类型上的生存期参数总是超过类型本身,而
shortlived
的生存期并不超过
foo
的生存期,因此很明显,我们不能使用该类型作为
foo
。如果
Foo
与
'a
是协变的,那将是它的结尾,并且您会得到一个错误。但是
Foo
与
'a
是互变的,因此我们可以将
shortlived
的生存期替换为更长的生存期。该生存期可以是任何超过
foo
生存期的生存期。请注意,“生存期”与“严格生存期”不同:区别是
'a: 'a
(
'a
超过
'a
)是正确的,但是
'a
严格超过
'a
是假的(即,生存期被说成是自身的生存期,但它并不存在)完全不存在)。因此,我们可能最终得到类型为
foo
的
Foo<'a>
,其中
'a
正是
foo
本身的生存期。
check(&foo, &outlived);
(这是第二个)。之所以进行编译,是因为
&outlived
被强制执行,因此缩短了生存期以匹配
foo
的生存期。这是正确的,因为
outlived
的生存期比
foo
更长,并且
check
的第二个参数是
'a
的协变变量,因为它是引用。
check(&foo, &shortlived);
为什么不编译?
foo
的生存期比
&shortlived
更长。
check
的第二个参数与
'a
协变,但它的第一个参数与
'a
协变,因为
Foo<'a>
是协变的。也就是说,这两个参数都试图朝相反的方向拉
'a
:
&foo
试图扩大
&shortlived
的生存期(这是非法的),而
&shortlived
试图缩短
&foo
的生存期(这也是非法的)。没有生存期可以统一这两个变量,因此该调用无效。
ensure_equal
的调用的语句都是有效的,因此它们将具有相同的类型。但是,如果将借用拆分为单独的
let
语句,该代码仍然有效,因此说明仍然有效。就是说,要使借阅有效,引用对象必须超过借用区域,因此,当我考虑生命周期参数时,我只关心引用对象的生存期,因此我考虑单独借用。
关于rust - 这个实例看似如何超过其自身的参数生存期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42637911/
我正在使用身份服务器 3 保护 Web 应用程序。我的应用程序分为 2 个 oidc 客户端,一个 ASP.Net MVC 客户端和一个使用 oidc-client javascript 库的 jav
我正在使用 Silex 构建一个小型后台项目,我正在使用 PdoSessionHandler 将 session 存储在数据库中。我已经成功地将 session 存储在数据库中,但似乎将 cookie
我是一名优秀的程序员,十分优秀!