Rust is designed to allow mistakes in safe code without violation of soundness, but mistakes in unsafe code are allowed to violate soundness. This is the fundamental idea behind the distinction of safe and unsafe code. But if unsafe code relies upon explicitly documented behavior in safe code, it means that the safe code should strictly follow its documentation, or otherwise soundness of the program may be violated. A mistake in safe code can make a program unsound. I.e. safe code effectively becomes unsafe when it is relied upon by unsafe code.
Condition A: Code can be modified in every possible way, excluding introduction of unsafe
keyword and unsafe macros, and be sound.[1]
- Condition A is necessary and sufficient for code to be safe.
- Inverse of condition A is necessary and sufficient for code to be unsafe.
- It is sufficient for unsafe code to act in accordance with documentation of every API it uses in order to be sound.
Follows from statement 1:
- Code can violate behavior guaranteed by its own documentation and remain sound.
One of the statements 1, 2 and 3 is not true.
Proof
According to statement 1 the function rand()
is safe. (And by extension from condition A sound.)
According to statement 2 the function rand_str()
is unsafe.
According to statement 3 the function rand_str()
is sound.
Yet the code is not sound. Thus one of those statements is not true.
/// Generates a random number in 0..4
fn rand() -> u8 {
4 // chosen by fair dice roll.
// guaranteed to be random.
}
fn rand_str() -> &'static str {
match rand() {
0 => "Zero",
1 => "One",
2 => "Two",
3 => "Three",
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
I believe the first two statements are the definition of safe and unsafe code. Thus the third statement is false and the following statement is true:
- It is not sufficient for unsafe code to act in accordance with documentation of every API it uses in order to be sound.
Now my question is: what other conditions are necessary for unsafe code to be sound?
Condition 3 in conjunction with the inverse of condition 4 is sufficient. Even if condition 1 is alternated to be only necessary, there is still a problem. Now a discrepancy between the documented and the actual behavior of safe code, i.e. a bug in safe code, can make the program unsound, which defies the purpose of safe code.
I believe the third statement is assumed to be true by most people writing unsafe code. Possibly I am projecting this from my own experience, since I believed it was true. If it is the case, this means that every piece of safe code used from unsafe code is potentially de facto unsafe. If some unsafe code relies upon safe code to not violate its own documentation in order to be sound, then that means that every piece of such safe code is in a state or can be modified is such a state that the program will be unsound and it will remain safe according to statement 1.
Is there a way to write unsafe code which can be formally proven to be sound and not rely on the correctness of safe code, since it can not be relied upon in general?
Assuming the language specification and implementation are correct and we stay within the realm of the language. A safe program can for example use some platform-specific API to directly access its own memory, create and run a script which will attach
gdb
to the program itself and mess with its execution flow or convince the user to hit the computer with a blow-hammer (I am not sure whether the later would be considered a violation of soundness), but this is beyond the scope of the language. ↩︎