3个版本
0.1.2 | 2019年7月22日 |
---|---|
0.1.1 | 2019年7月21日 |
0.1.0 | 2019年7月20日 |
#745 在 进程宏
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]
仅仅声明一个函数是不安全的,但本身并不能导致未定义的行为。
限制
由于过程宏工作方式的一个限制,存在一些小的限制
- 与泛型类型关联的函数,如果不引用
self
或Self
,则不能引用任何泛型类型。
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
}
- 在特质实现中,这仅在特质函数也标记为#[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