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

马佐

考虑以下代码,其中R包装了对根类型的引用还存储了某种类型N(avigate),它知道如何取消RT.

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 我想要做什么?
cotigao

特征类型对其通用参数是不变的。

考虑这个例子:

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 | |     }
   | |_____^

这里到底发生了什么?&selfwith 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是一个引用),它需要存活'rN需要它的输入参数与'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的定义在 上指定生命周期如果删除'ronN的输入参数的硬约束它将编译

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

具有不可变参数的自引用枚举

来自分类Dev

STD集合中引用的生命周期

来自分类Dev

指向不可变类型的共享指针具有值语义

来自分类Dev

对不可变数据的可变引用

来自分类Dev

在任务的生命周期内始终引用对象

来自分类Dev

具有可变字段的不可变对象

来自分类Dev

混合对HashMap的可变和不可变引用

来自分类Dev

通过引用临时传递功能的生命周期

来自分类Dev

结构中可变元素的生命周期

来自分类Dev

将具有可变引用的匹配项放入函数时的生命周期问题

来自分类Dev

为什么在使用弱引用时需要查找与listerner对象具有完全相同生命周期的对象?

来自分类Dev

使用生命周期参数为特征分离可变借位

来自分类Dev

在具有类似“复制”的语义的可变引用上创建包装器

来自分类Dev

获得对不可变变量的可变引用?

来自分类Dev

println内部的可变和不可变引用

来自分类Dev

xvalue的生命周期绑定到引用是否延长?

来自分类Dev

JavaScript可变生命周期

来自分类Dev

具有不同生命周期的不同Android应用程序组件如何共享对象?

来自分类Dev

对不可变数据的可变引用

来自分类Dev

静态类成员/类引用的生命周期?

来自分类Dev

混合对HashMap的可变和不可变引用

来自分类Dev

如何可变引用不可变值

来自分类Dev

右值引用的生命周期

来自分类Dev

当一个函数返回分配了相同生命周期的引用时,Rust Borrow Checker仅抱怨多次借用是可变的

来自分类Dev

lazy_static可变变量的生命周期问题

来自分类Dev

构建具有相同生命周期的多个引用的结构时,无法推断适当的生命周期

来自分类Dev

生命周期不匹配 - 返回引用的可变变量

来自分类Dev

函数中的可变生命周期 qs

来自分类Dev

闭包的 const 数组对 Rust 中具有生命周期参数的结构进行可变引用

Related 相关文章

  1. 1

    具有不可变参数的自引用枚举

  2. 2

    STD集合中引用的生命周期

  3. 3

    指向不可变类型的共享指针具有值语义

  4. 4

    对不可变数据的可变引用

  5. 5

    在任务的生命周期内始终引用对象

  6. 6

    具有可变字段的不可变对象

  7. 7

    混合对HashMap的可变和不可变引用

  8. 8

    通过引用临时传递功能的生命周期

  9. 9

    结构中可变元素的生命周期

  10. 10

    将具有可变引用的匹配项放入函数时的生命周期问题

  11. 11

    为什么在使用弱引用时需要查找与listerner对象具有完全相同生命周期的对象?

  12. 12

    使用生命周期参数为特征分离可变借位

  13. 13

    在具有类似“复制”的语义的可变引用上创建包装器

  14. 14

    获得对不可变变量的可变引用?

  15. 15

    println内部的可变和不可变引用

  16. 16

    xvalue的生命周期绑定到引用是否延长?

  17. 17

    JavaScript可变生命周期

  18. 18

    具有不同生命周期的不同Android应用程序组件如何共享对象?

  19. 19

    对不可变数据的可变引用

  20. 20

    静态类成员/类引用的生命周期?

  21. 21

    混合对HashMap的可变和不可变引用

  22. 22

    如何可变引用不可变值

  23. 23

    右值引用的生命周期

  24. 24

    当一个函数返回分配了相同生命周期的引用时,Rust Borrow Checker仅抱怨多次借用是可变的

  25. 25

    lazy_static可变变量的生命周期问题

  26. 26

    构建具有相同生命周期的多个引用的结构时,无法推断适当的生命周期

  27. 27

    生命周期不匹配 - 返回引用的可变变量

  28. 28

    函数中的可变生命周期 qs

  29. 29

    闭包的 const 数组对 Rust 中具有生命周期参数的结构进行可变引用

热门标签

归档