For example:
impl<T, A: Allocator> Vec<T, A> {
pub unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= self.capacity());
self.len = new_len;
}
unsafe fn append_elements(&mut self, other: *const [T]) {
let count = unsafe { (*other).len() };
self.reserve(count);
let len = self.len();
unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) };
self.len += count;
}
pub fn append(&mut self, other: &mut Self) {
unsafe {
self.append_elements(other.as_slice() as _);
other.set_len(0);
}
}
}
would be rewritten as:
impl<T, A: Allocator> Vec<T, A> {
pub unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= self.capacity());
self.len = new_len;
}
unsafe fn append_elements(&mut self, other: *const [T]) {
let count = (*other).len();
// ^ It doesn't make sense to wrap unsafe invocations in unsafe blocks
// just to declare "I'm performing unsafe operations here"
self.reserve(count);
let len = self.len();
ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count);
self.len += count;
}
pub fn append(&mut self, other: &mut Self) {
checked {
self.append_elements(other.as_slice() as _);
other.set_len(0);
}
}
}
According to The Rustonomicon 1.1. How Safe and Unsafe Interact:
The
unsafe
keyword has two uses: to declare the existence of contracts the compiler can't check, and to declare that a programmer has checked that these contracts have been upheld.You can use
unsafe
to indicate the existence of unchecked contracts on functions and trait declarations. On functions,unsafe
means that users of the function must check that function's documentation to ensure they are using it in a way that maintains the contracts the function requires. On trait declarations,unsafe
means that implementors of the trait must check the trait documentation to ensure their implementation maintains the contracts the trait requires.You can use
unsafe
on a block to declare that all unsafe actions performed within are verified to uphold the contracts of those operations. For instance, the index passed toslice::get_unchecked
is in-bounds.You can use
unsafe
on a trait implementation to declare that the implementation upholds the trait's contract. For instance, that a type implementingSend
is really safe to move to another thread.
Either of the following could be true:
- A whole lot of people (including people who wrote the std library) misunderstood when the keyword
unsafe
should be used - instead of unlocking the "unsafe privilege" like this:unsafe { unsafe_fn(); }
, it should actually mean "I've checked that everything required by the unsafe fn is always fulfilled" according to The Rustonomicon. It is why nounsafe
block is needed inunsafe fn
s, there's no point checking anything when you define anunsafe fn
which naturally means "I don't want to check anything cuz it's the developer's responsibility to check the conditions are met (unless the unchecked contracts of the outerunsafe fn
is different from that of the invokedunsafe fn
, in which case you still need to prove the "contract coersion" is safe). - The Rustonomicon should be revised to reflect how
unsafe
should be used now.
If the 1st case is true and what The Rustonomicon said is correct, I strongly recommend that the keyword unsafe
get split into unsafe
/ checked
to match different use cases as it is more intuitive.
Any ideas?