#unsafe #body #macro #block #mark #fn #i32

unsafe_fn

宏,用于标记函数为不安全,但其主体不是不安全的

3个版本

0.1.2 2019年7月22日
0.1.1 2019年7月21日
0.1.0 2019年7月20日

#745进程宏

MIT 许可证

18KB
248

unsafe_fn

属性宏,用于标记函数为不安全,但其主体不是不安全的

使用unsafe关键字标记函数会执行两个操作:

  • 函数只能从一个unsafe { ... }块中调用;
  • 函数的主体被包裹在一个unsafe块中,因此它可以执行不安全代码。

然而,在许多情况下,我们不希望整个主体都位于一个unsafe块内。

RFC 2585 讨论了这一点,并建议不再将不安全函数的主体视为不安全。

同时,这个宏允许通过#[unsafe_fn]属性来声明不安全函数,这样函数就是不安全的,但其主体不被视为不安全。

use unsafe_fn::unsafe_fn;

#[unsafe_fn]
fn add_to_ptr(a_ptr: *const i32, b: i32) -> i32 {
    let a = unsafe { *a_ptr }; // dereference in a unsafe block
    a + b   // safe code outside of the unsafe block
}

let x = &42 as *const i32;
// The function is unsafe and must be called in a unsafe block;
assert_eq!(unsafe { add_to_ptr(x, 1) }, 43);

为了保持一致性,您还可以在特性上使用unsafe_fn来声明一个不安全特性

// Equivalent to `unsafe trait UnsafeMarker {}`
#[unsafe_fn] trait UnsafeMarker {}

理由

摘自 RFC 2585 的动机部分

将函数标记为unsafe是Rust防止未定义行为的几个关键保护措施之一:即使程序员没有阅读文档,在非不安全块之外调用不安全函数(或执行其他不安全操作)也会导致编译错误,希望随后会阅读文档。

然而,当我们编写一个不安全 fn时,我们目前完全失去了这种保护:比如说,如果我意外地调用了offset而不是wrapping_offset[...],在编写不安全 fn时,这会发生而没有任何进一步的提示,因为不安全 fn的主体被视为一个不安全块。

[...]

使用更正式的术语,一个不安全块通常伴随着一个证明义务:程序员必须确保这段代码在当前上下文中实际上是安全的,因为编译器只是信任程序员能正确处理。相比之下,不安全 fn代表一个假设:作为此函数的作者,我做出一些假设,并期望调用者遵守这些假设。

通常,使用属性而不是关键字来标记不安全函数是有意义的:不安全关键字意味着代码是不安全的,在审查此代码时需要格外小心。而属性#[unsafe_fn]仅仅声明一个函数是不安全的,但本身并不能导致未定义的行为。

限制

由于过程宏工作方式的一个限制,存在一些小的限制

  1. 与泛型类型关联的函数,如果不引用selfSelf,则不能引用任何泛型类型。
struct X<T>(T);
impl<T> X<T> {
    #[unsafe_fn] // ok: reference self
    fn get(&self) -> &T { &self.0 }

    // Error! no refernces to 'self' or 'Self', so T cannot be used
    #[unsafe_fn]
    fn identity(x : &T) -> &T { x }
// error[E0401]: can't use generic parameters from outer function
}
  1. 在特质实现中,这仅在特质函数也标记为#[unsafe_fn]时才有效
trait Tr {
    #[unsafe_fn] fn fn1(&self);
    unsafe fn fn2(&self);
}
impl Tr for u32 {
    #[unsafe_fn] fn fn1(&self) {} // Ok
    #[unsafe_fn] fn fn2(&self) {} // Error: fn2 is not declared with #[unsafe_fn]
// error[E0407]: method `__unsafe_fn_fn2` is not a member of trait `Tr`
}

许可:MIT

依赖关系

~2MB
~46K SLoC