#unsafe #body #safety #fn #hygiene #proc-macro

require_unsafe_in_body

使unsafe fn在其函数体中仍需unsafe

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

Download history 2201/week @ 2024-03-13 1840/week @ 2024-03-20 1299/week @ 2024-03-27 1859/week @ 2024-04-03 1585/week @ 2024-04-10 2022/week @ 2024-04-17 2052/week @ 2024-04-24 1757/week @ 2024-05-01 1516/week @ 2024-05-08 1921/week @ 2024-05-15 1840/week @ 2024-05-22 2132/week @ 2024-05-29 1768/week @ 2024-06-05 1780/week @ 2024-06-12 1815/week @ 2024-06-19 2014/week @ 2024-06-26

7,769 每月下载量

MIT 许可证

18KB
302

#[require_unsafe_in_body]

一个过程宏属性,用于使unsafe fn在其体中仍需要unsafe块。

Latest version Documentation License

动机

想象一下有一个具有窄合同的函数,即一个在某些情况下可以触发未定义行为的函数(不正确的输入或调用上下文)。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 ≥ lenj ≥ len,它将中断;

  • 我们正在断言at_iat_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`.

用法

  1. 将此添加到您的 Cargo.toml

    [dependencies]
    require_unsafe_in_body = "0.2.0"
    
  2. 将此添加到您的 lib.rs(或 main.rs

    #[macro_use]
    extern crate require_unsafe_in_body;
    
  3. 然后您可以装饰

依赖关系

~1.5MB
~35K SLoC