> I have been told that "unsafe" affects code outside of that block, but hopefully stevelabnik may explain it better (again).
It's due to a couple of different things interacting with each other: unsafe relies on invariants that safe code must also uphold, and that the privacy boundary in Rust is the module.
Before we get into the unsafe stuff, I want you to consider an example. Is this Rust code okay?
struct Foo {
bar: usize,
}
impl Foo {
fn set_bar(&mut self, bar: usize) {
self.bar = bar;
}
}
No unsafe shenanigans here. This code is perfectly safe, if a bit useless.
Let's talk about unsafe. The canonical example of unsafe code being affected outside of unsafe itself is the implementation of Vec<T>. Vecs look something like this (the real code is different for reasons that don't really matter in this context):
struct Vec<T> {
ptr: *mut T,
len: usize,
cap: usize,
}
The pointer is to a bunch of Ts in a row, the length is the current number of Ts that are valid, and the capacity is the total number of Ts. The length and the capacity are different so that memory allocation is amortized; the capacity is always greater than or equal to the length.
That property is very important! If the length is greater than the capacity, when we try and index into the Vec, we'd be accessing random memory.
So now, this function, which is the same as Foo::set_bar, is no longer okay:
impl<T> Vec<T> {
fn set_len(&mut self, len: usize) {
self.len = len;
}
}
This is because the unsafe code inside of other methods of Vec<T> need to be able to rely on the fact that len <= capacity. And so you'll find that Vec<T>::set_len in Rust is marked as unsafe, even though it doesn't contain unsafe code. It still requires judicious use of to not introduce memory unsafety.
And this is why the module being the privacy boundary matters: the only way to set len directly in safe Rust code is code within the same privacy boundary as the Vec<T> itself. And so, that's the same module, or its children.