You can perfectly well write (code presenting) Undefined Behavior in Rust.
So, the idea is to have a subset of the language with which, ideally, (code presenting) Undefined Behavior cannot be written: this is what non-unsafe
Rust is for.
This, by the way, has usually required (using) a memory managed language (dynamically checking, for instance, each pointer dereference). The novelty of Rust is bringing properties / invariants such as the validity of a pointer to the type level (e.g., &T
references). This way, checking these invariants "boils down" to type checking.
Sadly, not all correct programs can be statically proven so by Rust (or any other language). In other words, there exist valid programs that cannot be written without unsafe
.
And that's precisely the point of the unsafe
keyword: it grants the programmer more freedom / expressibility in order for them to be able to write down such valid programs. But since these programs can no longer be proven (to be) correct by Rust, it is up to the programmer to "prove" its correctness.
Example
You cannot write
fn get_fast<T> (slice: &'_ [T], index: usize) -> &'_ T
{
unsafe {
// safety??
slice.get_unchecked(index)
}
}
because then, for instance, get_fast(&[], 0)
would be Undefined Behavior from within non-unsafe
Rust code!!
The correct implementation of get_fast
would be
/// # Safety
///
/// `slice.len()` must be greater than `index`
unsafe // Since the function cannot take all inputs "safely", it must be marked `unsafe`
fn get_fast<T> (slice: &'_ [T], index: usize) -> &'_ T
{
slice.get_unchecked(index)
}
Then if someone wants to use your function (potentially triggering UB with it), then they must do it from within an unsafe
block, meaning it was up to them to prove the call was right. For instance,
Correct unsafe
// Since this function is valid for all inputs, it can be marked non-`unsafe`
fn get<T> (array: &'_ [T; 256], index: u8) -> &'_ T
{
// since for all `index: u8`, `0 <= index < 256`, this indexing operation cannot be invalid.
// I thus wish to express an unchecked indexing operation.
// Such operation does not exist within non-unsafe Rust (how could it possibly exist?),
// I must thus use `unsafe`:
unsafe {
// safety (proof obligation):
// u8 is unsigned, thus index >= 0
// for all `x: u8`, `x <= core::u8::MAX`, and `core::u8::MAX = 255 < 256`
// thus `0 <= index < 256`; where `256 <= array.len()`
get_fast(array, usize::from(index))
}
}
Obligatory watching when talking about Undefined Behavior: