I am concerned with the behavior of the unsafe keyword regarding macro expressions as it allows unsafe code to be used implicitly in a codebase.
I originally submitted a request for a Clippy lint (https://github.com/rust-lang/rust-clippy/issues/3738) to detect macros which evaluate expressions in an unsafe block, like the following:
unsafe fn unsafe_operation() -> u8 {
1
}
macro_rules! value_equals_unsafe_operation {
($a:expr) => (
unsafe {
$a == unsafe_operation()
}
);
}
Where the above macro could be used to hide unsafe behavior in a program like so:
fn main() {
let a: [u8; 1] = [0; 1];
let x = value_equals_unsafe_operation(*a.get_unchecked(100));
println!("{}", x);
}
However, after some more thought, I decided to raise this issue with the Rust internals forum as I believe this could be solved better via a change to the Rust compiler itself.
The idea would be that for macros which evaluate an expression in an unsafe block, the unsafe keyword would only be applied to the code inside the macro itself, and not to any code passed in as an expression.
In this way, any code using the macro without any additional unsafe code in its expressions would be fine:
fn main() {
let x = value_equals_unsafe_operation!(0);
println!("{}", x);
}
However, if the expression itself requires any unsafe code, a second unsafe keyword would need to be added to the macro invocation, like so:
fn main() {
let a: [u8; 1] = [0; 1];
let x = value_equals_unsafe_operation!(unsafe { *a.get_unchecked(100) });
println!("{}", x);
}
I believe that requiring both places to add unsafe blocks more accurately describes that there is unsafe code used both inside the macro, and inside the expression for this single invocation of the macro.