Crates.io | unsafe_fn |
lib.rs | unsafe_fn |
version | 0.1.2 |
source | src |
created_at | 2019-07-20 14:09:13.14635 |
updated_at | 2019-07-22 18:58:39.52749 |
description | macro to mark a function as unsafe without its body being unsafe |
homepage | |
repository | https://github.com/ogoffart/unsafe_fn |
max_upload_size | |
id | 150346 |
size | 24,253 |
Attribute macro to mark a function as unsafe without its body being unsafe
Marking a function with the unsafe
keywords does two things:
unsafe { ... }
block;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 {}
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.
Due to a restriction in the way procedural macro works, there are a small limitation:
self
nor Self
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
}
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`
}
License: MIT