- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
当有特征对象传递给函数时如何处理生命周期?
struct Planet<T> {
i: T,
}
trait Spinner<T> {
fn spin(&self, value: T);
}
impl<T> Spinner<T> for Planet<T> {
fn spin(&self, value: T) {}
}
// foo2 fails: Due to lifetime of local variable being less than 'a
fn foo2<'a>(t: &'a Spinner<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
// foo1 passes: But here also the lifetime of local variable is less than 'a?
fn foo1<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
( Playground )
此代码导致此错误:
error[E0597]: `x` does not live long enough
--> src/main.rs:16:17
|
16 | t.spin(&x);
| ^ borrowed value does not live long enough
17 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 14:5...
--> src/main.rs:14:5
|
14 | fn foo2<'a>(t: &'a Spinner<&'a i32>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
foo1
的函数签名与 foo2
几乎相同.一个接收对 struct 的引用,另一个接收 trait 对象。
我读到这是高级特征边界的用武之地。将 foo2 修改为 foo2(t: &for<'a> Spinner<&'a i32>)
编译代码,但我不明白为什么。
为什么不会'a
收缩 x
?
引用 the Nomicon :
How on earth are we supposed to express the lifetimes on
F
's trait bound? We need to provide some lifetime there, but the lifetime we care about can't be named until we enter the body of call! Also, that isn't some fixed lifetime;call
works with any lifetime&self
happens to have at that point.
能否详细说明一下?
最佳答案
简而言之: foo1
编译是因为大多数类型的泛型参数都是变体,编译器仍然可以选择 Spinner
用于 t
的实现. foo2
不编译,因为特征在它们的通用参数和 Spinner
上是不变的impl 已经修复。
让我们看一下 foo
的第三个版本:
fn foo3<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
Spinner::<&'a i32>::spin(t, &x);
}
这会导致与您的 foo2
相同的错误.里面发生了什么?
写Spinner::<&'a i32>::spin
, 我们强制编译器使用 Spinner
的特定实现特征。以及Spinner::<&'a i32>::spin
的签名是fn spin(&self, value: &'a i32)
.时期。一生'a
由来电者提供; foo
不能选择它。因此,我们必须传递一个至少存在 'a
的引用。 .这就是编译器错误发生的原因。
那么为什么foo1
编译? 提醒一下:
fn foo1<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
在这里,一生'a
也是调用者给出的,不能被foo1
选择. 但是,foo1
可以选择 Spinner
的哪个含义使用!注意 impl<T> Spinner<T> for Planet<T>
基本上定义了无限多的特定实现(每个 T
一个)。所以编译器也知道 Planet<&'x i32>
执行Spinner<&'x i32>
(其中'x
是函数中x
的具体生命周期)!
现在编译器只需要弄清楚它是否可以转换 Planet<&'a i32>
进入Planet<&'x i32>
.是的,它可以,因为 most types are variant over their generic parameters因此 Planet<&'a i32>
是 Planet<&'x i32>
的子类型如果'a
是 'x
的子类型(它是)。所以编译器只是“转换”t
至 Planet<&'x i32>
然后是 Spinner<&'x i32>
可以使用impl。
太棒了!但现在是主要部分:为什么不 foo2
然后编译? 再次提醒一下:
fn foo2<'a>(t: &'a Spinner<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
再次,'a
由调用者和 foo2
给出不能选择它。不幸的是,现在我们已经有了具体的实现!即Spinner<&'a i32>
.我们不能假设我们传递的东西也实现了 Spinner<&'o i32>
对于任何其他生命 'o != 'a
! Traits are invariant over their generic parameters .
换句话说:我们知道我们有东西可以处理至少与'a
一样长的引用。 .但是我们不能假设我们得到的东西也可以处理短于 'a
的生命周期。 !
举个例子:
struct Star;
impl Spinner<&'static i32> for Star {
fn spin(&self, value: &'static i32) {}
}
static SUN: Star = Star;
foo2(&SUN);
在这个例子中,'a
的 foo2
是'static
.事实上,Star
工具 Spinner
仅适用于 'static
引用 i32
.
顺便说一下:这不是特征对象特有的!让我们看看 foo
的第四个版本:
fn foo4<'a, S: Spinner<&'a i32>>(t: &'a S) {
let x: i32 = 10;
t.spin(&x);
}
再次出现同样的错误。问题又是 Spinner
impl 已经修复了!对于 trait 对象,我们只知道 S
工具 Spinner<&'a i32>
,不一定更多。
使用更高等级的特征界限解决了这个问题:
fn foo2(t: &for<'a> Spinner<&'a i32>)
和
fn foo4<S: for<'a> Spinner<&'a i32>>(t: &S)
希望从上面的解释中可以清楚地看出,这是可行的,因为我们使用了 Spinner
的特定含义。不再固定了!相反,我们再次有无限多的 impl 可供选择(每个 'a
一个)。因此我们可以选择 'a == 'x
的 impl .
关于rust - 为什么作为参数传递的特征对象的生命周期需要更高等级的特征界限,而结构不需要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50946525/
我试图在 for 循环中更新我的 for 循环的上限。 例子 numvar = 3 for i=2 To numvar Step 2 'code... numvar = numvar
给定f(x, y)和g(n): def f(x, y): if x < 1 or y < 1: return 1 return f(x - 1, y - 1) + f(
我是一名优秀的程序员,十分优秀!