为什么借用检查器会抱怨这些不同切片的生命周期?

Mar*_*cus 2 vector lifetime slice rust borrow-checker

为什么在下面的代码中,使用数组切片有效,但使用 a 的切片Vec无效?

use rand::{rngs::adapter::ReadRng, RngCore};
use std::io::Read;

struct MyRng {
    rng: Box<dyn RngCore>,
}

pub fn main() {
    // Version 1: error
    //
    let data = Vec::<u8>::new();
    let data_slice = data.as_slice();

    // Version 2: works
    //
    // let data_slice = &[0_u8][..];

    // Version 3: error (!?!)
    //
    // let data = [0_u8];
    // let data_slice = &data[..];

    let read = Box::new(data_slice) as Box<dyn Read>;
    let rng = Box::new(ReadRng::new(read));

    // With this commented out, all versions work.
    MyRng { rng };
}
Run Code Online (Sandbox Code Playgroud)

有几件事让我感到困惑:

  • 如果都在同一范围内,这三种方法之间有什么区别?
  • 该错误表示data借用时已删除,但指向范围的末尾 - 到那时不是所有内容都删除了吗?
  • 为什么如果我删除MyRng实例化,一切正常?

kfe*_*v91 5

关于终身省略的 Rust 参考:

如果 trait 没有生命周期界限,那么生命周期是在表达式中推断出来的,并且在表达式'static之外。

因此,默认情况下,装箱的 trait 对象会'static受到限制。所以这个结构:

struct MyRng {
    rng: Box<dyn RngCore>,
}
Run Code Online (Sandbox Code Playgroud)

实际上扩展到这个:

struct MyRng {
    rng: Box<dyn RngCore + 'static>,
}
Run Code Online (Sandbox Code Playgroud)

这迫使您生成拥有的类型或'static引用以满足界限。但是,您可以'static通过使结构通用来完全退出隐式绑定,然后编译所有不同版本的代码:

use rand::{rngs::adapter::ReadRng, RngCore};
use std::io::Read;

struct MyRng<'a> {
    rng: Box<dyn RngCore + 'a>,
}

pub fn main() {
    // Version 1: now works
    let data = Vec::<u8>::new();
    let data_slice = data.as_slice();
    let read = Box::new(data_slice) as Box<dyn Read>;
    let rng = Box::new(ReadRng::new(read));
    let my_rng = MyRng { rng };

    // Version 2: still works
    let data_slice = &[0_u8][..];
    let read = Box::new(data_slice) as Box<dyn Read>;
    let rng = Box::new(ReadRng::new(read));
    let my_rng = MyRng { rng };

    // Version 3: now works
    let data = [0_u8];
    let data_slice = &data[..];
    let read = Box::new(data_slice) as Box<dyn Read>;
    let rng = Box::new(ReadRng::new(read));
    let my_rng = MyRng { rng };
}
Run Code Online (Sandbox Code Playgroud)

操场


要更直接地回答您的个人问题:

如果都在同一范围内,这三种方法之间有什么区别?

范围和生命周期不是一回事,但 3 种方法之间的主要区别在于方法 #2 创建了一个静态切片。当你将一些&T引用硬编码到你的代码中而不引用任何变量拥有的任何数据时,它就会被写入二进制文件的只读段并获得'static生命周期。

该错误表示数据在借用时被丢弃,但指向范围的末尾 - 到那时不是所有的东西都被丢弃了吗?

是的,但是根据定义,您的类型要求传递的值受'static生命周期的限制,并且由于方法 #1 和 #3 不会产生这样的值,编译器会拒绝代码。

为什么如果我删除MyRng实例化,一切正常?

因为MyRng结构体的定义没有任何问题,只有当您尝试错误地实例化它时,编译器才会抱怨。