明确的终身注释玩具示例
我正在尝试找出生锈的终身问题,并在将其煮沸后,我意识到我不知道如何明确注释r
,x2 和
_arr
in foo
:
struct X<'a> {
_v: &'a mut i32,
}
fn main() {
let mut v1 = 0;
let x1 = X { _v: &mut v1 };
foo(x1);
}
fn foo(x1: X) {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
}
I'm trying to figure out a Rust lifetime issue and after boiling it down a bunch, I realized that I have no idea how I would explicitly annotate the lifetimes of r
, x2
, and _arr
in foo
:
struct X<'a> {
_v: &'a mut i32,
}
fn main() {
let mut v1 = 0;
let x1 = X { _v: &mut v1 };
foo(x1);
}
fn foo(x1: X) {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我将摆脱一个假设,即您的代码部分被切断的部分是:
在这种情况下,让我们稍微解剖
foo
中的情况。混乱可能来自以下事实:
x1
似乎具有模棱两可的寿命。这是因为我们没有在x1:x
中明确指定寿命。这是一个生锈的2018成语,我个人建议不要这样做。您可以将#![deny(rust_2018_idioms)]
添加到板条箱根的顶部,编译器将向您指出这些歧义,并迫使您更加明确。这里发生的事情是函数声明被删除到以下内容:现在暗示Lifetime
'x1
至少通过foo
的主体扩展,这就是有意义,因为它必须这样做。如果在'x1
的生活中被释放在foo
的中间安全。话虽如此,让我们重新审视这一行:
我们知道
x2
是类型x&lt;'r&gt;
,并且该'r
在大多数到foo
的结尾(因为'r
不超过'v2
和'v2
扩展到foo
的结尾)。此外,我们知道x1
是类型x&lt;'x1&gt;
,并且该'x1
至少扩展到的末尾foo
。这意味着'x1
至少与'r
一样长。由于寿命是协变量的,因此这意味着x&lt;'x1&gt;
是x&lt;'r&gt;
的子类型,因为只要我们需要类型x&lt;'r&gt; 我们可以将其安全地替换为
x&lt;'x1&gt;
的一种。因此,发生的是_arr
被赋予类型[X&lt;'r&gt;;; 2]
,并且在施工后,x1
上的寿命缩短为'r
。我们实际上可以检验此假设,以查看是否正确。如果不允许编译器缩短一生,将会发生什么?换句话说,如果
x
上的寿命不是协变量怎么办。如果我们修改x
,则其寿命类型参数是不变的:果然,在适当的位置添加
_invariant
字段之后以下错误:现在我们如何知道编译器不将Lifetime
'r
'to'x1
首先扩展到?好吧,如果是这样做的,那么我们可以修改foo
执行以下操作,这明确会导致免费使用时:如果您尝试上述代码,则可以肯定的是,它无法使用原因是我们正在返回对本地变量的引用。此外,如果您尝试返回
_arr [0] ._ v
,则获得完全相同的错误。亚型和差异可能很难掌握,这不是您需要充分理解的有效的Rust程序员。尽管如此,它还是非常有趣的,您可以了解更多有关它的信息在这里 Rustonomicon。
I'm going to work off of the assumption that the part of your code that got cut off is the following:
In that case, let's dissect what's going on in
foo
a little bit.The confusion probably comes from the fact that
x1
seems to have an ambiguous lifetime in its type. This is because we didn't explicitly specify the lifetime inx1: X
. This is a Rust 2018 idiom, and I personally recommend not doing this. You can add#![deny(rust_2018_idioms)]
to the top of your crate root and the compiler will point out these ambiguities to you and force you to be more explicit. What happens here is that the function declaration gets de-sugared to the following:Now it is implied that the lifetime
'x1
extends at least through the body offoo
, and this makes sense because it kind of has to. If something which lived for'x1
was freed in the middle offoo
(disregarding how something like that could even happen), then that would defeat the point of using lifetimes for memory safety.So that being said, let's revisit this line:
We know that
x2
is of the typeX<'r>
and that'r
extends at most to the end offoo
(since'r
is no longer than'v2
and'v2
extends to the end offoo
). Moreover, we know thatx1
is of the typeX<'x1>
and that'x1
extends at least to the end offoo
. This means that'x1
is at least as long as'r
. Since lifetimes are covariant, this means thatX<'x1>
is a sub-type ofX<'r>
, since whenever we require a value of the typeX<'r>
we can safely substitute in one of typeX<'x1>
instead. So what happens is_arr
is given the type[X<'r>; 2]
, and upon construction the lifetime onx1
is shortened to'r
.We can actually test this hypothesis to see if it's correct. What would happen if the compiler wasn't allowed to shorten that lifetime? In other words, what if the lifetime on
X
was not covariant. If we modifyX
as follows then its lifetime type parameter is made invariant:And sure enough, after adding the
_invariant
field in the appropriate places to make the code compile, we receive the following error:Now how do we know the compiler wasn't extending the lifetime
'r
to'x1
in the first place? Well if it was doing that, then we could modifyfoo
to do the following, which would unequivocally cause a use-after-free:And sure enough if you try the code above it fails to compile with the reason given being that we're returning a reference to a local variable. Moreover, if you try returning
_arr[0]._v
, then you get the exact same error.Sub-typing and variance can be pretty hard to grasp and it's not something you need to fully understand to be an effective Rust programmer. Nonetheless, it is very interesting and you can learn more about it here in the Rustonomicon.