10次发布
0.3.2 | 2020年10月28日 |
---|---|
0.3.1 | 2020年10月27日 |
0.2.1 | 2020年3月2日 |
0.2.0 | 2019年8月30日 |
0.1.2 | 2019年8月21日 |
30 in #fn
7,769 每月下载量
18KB
302 行
#[require_unsafe_in_body]
一个过程宏属性,用于使unsafe fn
在其体中仍需要unsafe
块。
动机
想象一下有一个具有窄合同的函数,即一个在某些情况下可以触发未定义行为的函数(不正确的输入或调用上下文)。Rust安全性设计要求,如果该函数是公共API的一部分,则必须将其标记为unsafe
,即使它不是,也强烈建议这样做(代码将更容易维护)
/// Swaps two values of a mutable slice, without checking that the indices are valid.
pub
unsafe // narrow contract
fn swap_indices_unchecked<T> (slice: &'_ mut [T], i: usize, j: usize)
{
let at_i: *mut T = slice.get_unchecked_mut(i);
let at_j: *mut T = slice.get_unchecked_mut(j);
::core::ptr::swap_nonoverlapping(at_i, at_j, 1)
}
正如你所看到的,当一个函数被标记为unsafe
时,函数体不再需要围绕最微妙之处进行特殊的unsafe
块。例如,在这种情况下,可能并不明显存在两个不同的unsafe
情况
-
我们正在进行未经检查的索引,如果
i ≥ len
或j ≥ len
,它将中断; -
我们正在断言
at_i
和at_j
不发生别名,如果i = j
,它将中断。
由于滥用这些不变量是不合逻辑的,所以如果可以明确地指出每个不变量在哪里或可能被使用,那就更好了
/// Swaps two values of a mutable slice, without checking that the indices are valid.
///
/// # Safety
///
/// The indices must be valid:
///
/// - `i ≠ j`
///
/// - `i < slice.len()`
///
/// - `j < slice.len()`
#[allow(unused_unsafe)]
pub
unsafe // narrow contract
fn swap_indices_unchecked<T> (slice: &'_ mut [T], i: usize, j: usize)
{
let at_i: *mut T = unsafe {
// Safety: `i < slice.len()`
debug_assert!(i < slice.len());
slice.get_unchecked_mut(i)
};
let at_j: *mut T = unsafe {
// Safety: `j < slice.len()`
debug_assert!(j < slice.len());
slice.get_unchecked_mut(j)
};
unsafe {
// Safety: `at_i` and `at_j` do not alias since `i ≠ j`
debug_assert_ne!(i, j);
::core::ptr::swap_nonoverlapping(at_i, at_j, 1);
}
}
遗憾的是,由于这些unsafe
块不是必需的,它们不仅会导致unused_unsafe
警告,还可能被错误地遗漏,而Rust不会发出任何警告。
这就是#[
require_unsafe_in_body
]
解决的问题
#[
require_unsafe_in_body
]
"自动移除"了函数体内部的固有unsafe
(卫生) 性,因此需要在内部使用unsafe
范围。
示例
以下代码
#[macro_use]
extern crate require_unsafe_in_body;
/// Swaps two values of a mutable slice, without checking that the indices are valid.
#[require_unsafe_in_body]
pub
unsafe // narrow contract
fn swap_indices_unchecked<T> (slice: &'_ mut [T], i: usize, j: usize)
{
let at_i: *mut T = slice.get_unchecked_mut(i);
let at_j: *mut T = slice.get_unchecked_mut(j);
::core::ptr::swap_nonoverlapping(at_i, at_j, 1);
}
会导致编译器错误
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> example.rs:11:24
|
11 | let at_i: *mut T = slice.get_unchecked_mut(i);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> example.rs:12:24
|
12 | let at_j: *mut T = slice.get_unchecked_mut(j);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> example.rs:13:5
|
13 | ::core::ptr::swap_nonoverlapping(at_i, at_j, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
For more information about this error, try `rustc --explain E0133`.
用法
-
将此添加到您的
Cargo.toml
[dependencies] require_unsafe_in_body = "0.2.0"
-
将此添加到您的
lib.rs
(或main.rs
)#[macro_use] extern crate require_unsafe_in_body;
-
然后您可以装饰
-
函数,使用
#[
require_unsafe_in_body
]
属性; -
方法,使用
#[
require_unsafe_in_bodies
]
属性 应用到封装的impl
或trait
块。
-
依赖关系
~1.5MB
~35K SLoC