我正在漫步
core::time
,并对Nanoseconds
的实施感到惊讶。
为什么实例化它需要一个 unsafe
块?
我理解有关 Nanoseconds
范围限制的评论(必须是 < NANOS_PER_SEC
),但是 unsafe
在这种情况下有何帮助?
如果我将代码复制到自定义包中(编译器内部结构除外,它们不稳定):
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
struct Nanoseconds(u32);
impl Default for Nanoseconds {
#[inline]
fn default() -> Self {
// SAFETY: 0 is within the valid range
unsafe { Nanoseconds(0) }
}
}
fn main() {
let nanos = Nanoseconds(0);
println!("Nanos: {}", nanos.0);
}
我从 rustc 收到以下警告:
warning: unnecessary `unsafe` block
--> src/main.rs:9:9
|
9 | unsafe { Nanoseconds(0) }
| ^^^^^^ unnecessary `unsafe` block
|
= note: `#[warn(unused_unsafe)]` on by default
warning: `test_nanos` (bin "test_nanos") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/test_nanos`
Nanos: 0
由于
Nanoseconds
只是一个元组结构,因此应该可以在没有周围 unsafe
块的情况下实例化它,对吗?
因为
#[rustc_layout_scalar_valid_range_*(…)]
属性告诉编译器它可以基于存储在 Nanoseconds
中的值永远不会超出它们指定的范围的假设来优化 1代码。因此,创建一个值超出范围的
Nanoseconds
是未定义行为 (UB):
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
#[rustc_layout_scalar_valid_range_end(999_999_999)]
struct Nanoseconds(u32);
- 即使在私人领域和当地人中也会产生无效值。每当将值分配给某个位置或从某个位置读取值、传递给函数/基元操作或从函数/基元操作返回值时,就会“生成”值。以下值无效(在其各自的类型中):
[…]
和NonNull<T>
。NonZero*
注意:
通过不稳定的rustc
属性实现了这一点。rustc_layout_scalar_valid_range_*
允许编译器假设
Nanoseconds
中的值始终在 0..=999_999_999
内并进行相应优化。
这类似于占据整个字节的
bool
,但仅允许值0
和1
,而2..=255
是无效值。
1) 例如,它可以省略像
nanos.0 <= 999_999_999
这样的边界检查,或者它可以使用 1_000_000_000
及以上作为 enum
中的 nieche