[−][src]Crate unsafe_fn
Attribute macro to mark a function as unsafe without its body being unsafe
Marking a function with the unsafe
keywords does two things:
- The function may only be called from an
unsafe { ... }
block; - and the body of the function is itself wrapped in a
unsafe
block, so it can perform unsafe code.
In many case however, it is not desirable to have the full body
inside an unsafe
block.
RFC 2585 discusses that and suggests to no longer treat the body of a unsafe function as unsafe.
In the mean time, this macro allows to declare a unsafe function
with a #[unsafe_fn]
attribute, so that the function is unsafe,
but its body is not considered as unsafe.
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);
For consistency, it is also possible to use the unsafe_fn
on traits
to declare an unsafe trait
// Equivalent to `unsafe trait UnsafeMarker {}` #[unsafe_fn] trait UnsafeMarker {}
Rationale
From the motivation section of RFC 2585:
Marking a function as
unsafe
is one of Rust's key protections against undefined behavior: Even if the programmer does not read the documentation, calling anunsafe
function (or performing another unsafe operation) outside an unsafe block will lead to a compile error, hopefully followed by reading the documentation.However, we currently entirely lose this protection when writing an
unsafe fn
: If I, say, accidentally call offset instead of wrapping_offset [..] this happens without any further notice when I am writing anunsafe fn
because the body of anunsafe fn
is treated as anunsafe
block.[...]
Using some more formal terminology, an
unsafe
block generally comes with a proof obligation: The programmer has to ensure that this code is actually safe to execute in the current context, because the compiler just trusts the programmer to get this right. In contrast,unsafe fn
represents an assumption: As the author of this function, I make some assumptions that I expect my callees to uphold.
In general, using an attribute instead of a keyword to mark unsafe function make
sense: the unsafe
keyword would mean that the code is unsafe and extra care
need to be used when reviewing this code. While the attribute #[unsafe_fn]
merly
declare a function as unsafe, but cannot by itself cause undefined behavior.
Limitations
Due to a restriction in the way procedural macro works, there are a small limitation:
- associated functions of a generic type that reference neither
self
norSelf
cannot reference any of the generic type.
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 }
- Within trait implementation this only work if the trait function was also marked with #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` }
Attribute Macros
safe_body | Make the body of an unsafe function not allowed to call unsafe code without adding unsafe blocks |
unsafe_fn | Mark a function as unsafe without its body being in an unsafe block |