考虑以下代码,其中R
包装了对根类型的引用。还存储了某种类型N
(avigate),它知道如何取消R
对T
.
use std::ops::Deref;
struct Wrapper<'r, R, N, T>
where
N: Fn(&'r R) -> &T,
T: 'static,
{
r: &'r R,
n: N,
}
impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
where
N: Fn(&'r R) -> &T,
T: 'static,
{
type Target = T;
fn deref(&self) -> &T {
let r: &'r R = self.r;
let t: &'r T = (self.n)(r);
t
}
}
现在,如果我们将引用类型r: &'r R,
更改为可变的,r: &'r mut R,
它将不再起作用:
use std::ops::Deref;
struct Wrapper<'r, R, N, T>
where
N: Fn(&'r R) -> &T,
T: 'static,
{
r: &'r mut R,
n: N,
}
impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
where
N: Fn(&'r R) -> &T,
T: 'static,
{
type Target = T;
fn deref(&self) -> &T {
let r: &'r R = self.r;
let t: &'r T = (self.n)(r);
t
}
}
错误:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/lib.rs:21:24
|
21 | let r: &'r R = self.r;
| ^^^^^^
|
note: ...the reference is valid for the lifetime 'r as defined on the impl at 13:6...
--> src/lib.rs:13:6
|
13 | impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 20:5
--> src/lib.rs:20:5
|
20 | / fn deref(&self) -> &T {
21 | | let r: &'r R = self.r;
22 | | let t: &'r T = (self.n)(r);
23 | | t
24 | | }
| |_____^
我们用 nll 得到了更好的错误信息:
error: lifetime may not live long enough
--> src/lib.rs:21:16
|
13 | impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
| -- lifetime `'r` defined here
...
20 | fn deref(&self) -> &T {
| - let's call the lifetime of this reference `'1`
21 | let r: &'r R = self.r;
| ^^^^^ type annotation requires that `'1` must outlive `'r
我在 deref 中注释了生命周期,以确保我与编译器在生命周期方面处于同一轨道上。nll 消息特别有趣,因为它说它需要&self
比'r
.
但这对我来说没有意义,因为如果我们在 deref 上注释生命周期,它应该如下所示:
fn deref<'1>(&'1 self) -> &'1 T;
而是要求'r: '1
,这是由隐式给出的Wrapper<'r, ...>
这种直觉在第一个例子中似乎成立,但在第二个例子中不可变引用成立。
所以有两个问题向我展开:
self.r
不可变会有所不同?r
无论如何,我无法可变地访问,因为它&self
是不可变的。1.
一个基本的限制,还是可以以某种方式对代码进行注释以告诉 rustc 我想要做什么?特征类型对其通用参数是不变的。
考虑这个例子:
struct Test<'a, F: Fn(&'a i32)> {
i: &'a i32,
f: F,
}
fn main() {
let i = 1i32;
let t = Test { i: &i, f: |&_| {} };
{
let j = 2i32;
(t.f)(&j);
}
println!("{:?}", t.i);
}
这将给出错误:
error[E0597]: `j` does not live long enough
--> src/main.rs:12:15
|
12 | (t.f)(&j);
| ^^ borrowed value does not live long enough
13 | }
| - `j` dropped here while still borrowed
14 |
15 | println!("{:?}", t.i);
| --- borrow later used here
如您所见,该类型Test<'a ...
并没有统一到更短的生命周期,j
因为Test
包含一个 trait impl 类型N
(静态调度)。因此,它将在 上保持不变'a
,因此'a
无法缩短。但j
不为'a
,因此错误。
转到您的问题,让我们看一下您的代码的最小版本:
struct Wrapper<'r, R, N>
where
N: Fn(&'r R),
{
r: &'r mut R,
n: N,
}
impl<'r, R, N> Wrapper<'r, R, N>
where
N: Fn(&'r R),
{
fn myderef(&self) {
(self.n)(self.r)
}
}
这将给出相同的错误:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/lib.rs:14:18
|
14 | (self.n)(self.r)
| ^^^^^^
|
note: ...the reference is valid for the lifetime 'r as defined on the impl at 9:6...
--> src/lib.rs:9:6
|
9 | impl<'r, R, N> Wrapper<'r, R, N>
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 13:5
--> src/lib.rs:13:5
|
13 | / fn myderef(&self) {
14 | | (self.n)(self.r)
15 | | }
| |_____^
这里到底发生了什么?&self
with lifes 将是 type&'shorter_lifetime Wrapper<'r, R, N>
而不是&'shorter_lifetime Wrapper<'shorter_lifetime, R, N>
。'r
不会被缩短为'shorter_lifetime
因为Wrapper
它的通用生命周期参数是不变的,'r
因为N
.
现在我们知道参数类型到底&self
是什么,让我们看看myderef()
. 特征类型N
(静态分派)使用self.r
. 但是self.r
是一个可变引用,当传递给(self.r)()
. 所以现在你有一个可变引用,它在另一个引用后面(self
是一个引用),它需要存活'r
(N
需要它的输入参数与'r
定义一样具有生命周期),因此&self
也需要存活'r
. 但是&self
的生命周期是'shorter_lifetime
,因此是错误的。
换句话说,如果你有&'a & 'b mut T
('a
和之间没有子类型关系'b
)作为函数的输入参数,并且编译器允许你重新借用内部引用并返回它,那么就违反了借用规则,因为&mut T
已经是后面有参考。外部引用“拥有”内部引用,主要是因为内部引用是可变的,并且函数需要保证外部引用至少在重新借用内部(可变)引用时保留,否则在函数调用之后将是可变引用的不止一个所有者。
例如,以下代码将无法编译:
fn test<'a, 'b> (i:&'a &'b mut i32) -> &'b i32 {
&**i
}
但是这个会:
fn test<'a:'b, 'b> (i:&'a &'b mut i32) -> &'b i32 {
&**i
}
因为有一个保证'a
至少可以存活多久'b
。
如果内部引用是不可变的,那么前者也会编译,因为您可以有多个不可变引用。没有外部引用“拥有”内部引用的概念。
为了编译最小版本,我们必须告诉编译器它&self
也适用于'r
. 要么删除的输入参数'r
上的硬约束N
(生命周期省略)。
在您的示例中,deref()
不允许您&self
根据Deref
的定义在 上指定生命周期。如果删除'r
onN
的输入参数的硬约束,它将编译
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句