为什么 borrow checker 抱怨这些不同切片的生命周期?
Why does borrow checker complain about the lifetimes of these different slices?
为什么在下面的代码中,使用数组切片有效,但使用 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 };
}
有几件事让我很困惑:
- 如果都在同一范围内,这三种方法有什么区别?
- 错误说
data
在借用时被丢弃,但指向范围的末尾 - 到那时不是所有东西都被丢弃了吗?
- 为什么如果我删除
MyRng
实例化,一切正常?
The Rust Reference on Lifetime Elision:
If the trait has no lifetime bounds, then the lifetime is inferred in expressions and is 'static
outside of expressions.
因此,默认情况下,盒装特征对象会受到 'static
约束。所以这个结构:
struct MyRng {
rng: Box<dyn RngCore>,
}
实际展开为:
struct MyRng {
rng: Box<dyn RngCore + 'static>,
}
这会强制您生成一个拥有的类型或一个 '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 };
}
更直接地回答您的个别问题:
What's the difference between the three approaches, if all are in the same scope?
范围和生命周期不是一回事,但这 3 种方法之间的主要区别在于方法 #2 创建静态切片。当您将某些 &T
引用硬编码到您的代码中而不引用任何变量拥有的任何数据时,它将被写入二进制文件的只读段并获得 'static
生命周期。
The error says that data is dropped while borrowed, but points to the end of the scope - isn't everything dropped by then?
是的,但是根据定义,您的类型要求传递的值受 'static
生命周期的限制,并且由于方法 #1 和 #3 不会产生此类值,编译器会拒绝代码。
Why if I remove the MyRng
instantiation, everything works fine?
因为您的 MyRng
结构的定义没有任何问题,只有当您尝试错误地实例化它时,编译器才会抱怨。
[0_u8]
是一个常量,所以它经历了 rvalue static promotion,允许对它的引用有一个静态生命周期,所以它不会被丢弃。您的代码失败是因为您有一个切片借用了局部变量 data
但试图从中创建一个 MyRng
变量。
为什么在下面的代码中,使用数组切片有效,但使用 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 };
}
有几件事让我很困惑:
- 如果都在同一范围内,这三种方法有什么区别?
- 错误说
data
在借用时被丢弃,但指向范围的末尾 - 到那时不是所有东西都被丢弃了吗? - 为什么如果我删除
MyRng
实例化,一切正常?
The Rust Reference on Lifetime Elision:
If the trait has no lifetime bounds, then the lifetime is inferred in expressions and is
'static
outside of expressions.
因此,默认情况下,盒装特征对象会受到 'static
约束。所以这个结构:
struct MyRng {
rng: Box<dyn RngCore>,
}
实际展开为:
struct MyRng {
rng: Box<dyn RngCore + 'static>,
}
这会强制您生成一个拥有的类型或一个 '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 };
}
更直接地回答您的个别问题:
What's the difference between the three approaches, if all are in the same scope?
范围和生命周期不是一回事,但这 3 种方法之间的主要区别在于方法 #2 创建静态切片。当您将某些 &T
引用硬编码到您的代码中而不引用任何变量拥有的任何数据时,它将被写入二进制文件的只读段并获得 'static
生命周期。
The error says that data is dropped while borrowed, but points to the end of the scope - isn't everything dropped by then?
是的,但是根据定义,您的类型要求传递的值受 'static
生命周期的限制,并且由于方法 #1 和 #3 不会产生此类值,编译器会拒绝代码。
Why if I remove the
MyRng
instantiation, everything works fine?
因为您的 MyRng
结构的定义没有任何问题,只有当您尝试错误地实例化它时,编译器才会抱怨。
[0_u8]
是一个常量,所以它经历了 rvalue static promotion,允许对它的引用有一个静态生命周期,所以它不会被丢弃。您的代码失败是因为您有一个切片借用了局部变量 data
但试图从中创建一个 MyRng
变量。