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
unsafekeyword 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
unsafeto indicate the existence of unchecked contracts on functions and trait declarations. On functions,unsafemeans 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,unsafemeans that implementors of the trait must check the trait documentation to ensure their implementation maintains the contracts the trait requires.You can use
unsafeon 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_uncheckedis in-bounds.You can use
unsafeon a trait implementation to declare that the implementation upholds the trait's contract. For instance, that a type implementingSendis 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
unsafeshould 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 nounsafeblock is needed inunsafe fns, there's no point checking anything when you define anunsafe fnwhich 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 fnis 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
unsafeshould 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?