gpt4 book ai didi

rust - 读取可变引用与不可变引用具有不同的生命周期语义

转载 作者:行者123 更新时间:2023-11-29 08:32:06 25 4
gpt4 key购买 nike

考虑以下代码,其中对根类型的引用 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, ...> 隐式给出

这种直觉似乎适用于第一个示例,但不适用于具有不可变引用的第二个示例。

所以我有两个问题:

  1. 为什么 self.r 会有所不同?是不可变的吗?我无法访问 r反正是可变的,因为&self是不可变的。
  2. 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包含特征实现类型 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 lifetimes 的类型为 &'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 的硬约束在 N的输入参数,it will compile

关于rust - 读取可变引用与不可变引用具有不同的生命周期语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54370530/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com