[T]::split_at_mutVec<T> growingWhat does this program print?
5.88545e-44
Accesses past the end of an array are undefined behavior
Why not simply define what happens on an out-of-bounds memory access?
operator[] in Rust requires bounds checking
unsafe Rustunsafe keywordunsafe function, we need an unsafe {} blockunsafe {} blocks with a //SAFETY comment!unsafe is dangerousunsafe fn will have safety guarantees: Rules that you have to follow in order to make it safe to use this functionunsafeunsafe? (1/4)Calling a C function:
unsafe? (2/4)Accessing a non-existing element in a HashMap:
unsafe? (3/4)Allocating a block of memory on the heap:
unsafe? (4/4)Accessing a mutable global variable:
unsafe code*const T and *mut Tstd::ptr module
The precise rules for validity are not determined yet0 or 1 for a bool, nothing else)*ptr::readptr::writeLet’s implement Vec<T> :)
Vec<T> v1Box<[T]>?Vec<T> v2unsafe:
fn push_first(&mut self, element: T) {
const INITIAL_CAPACITY: usize = 4;
let layout = Layout::array::<T>(INITIAL_CAPACITY)
.expect("Invalid memory layout");
// SAFETY: We check for null afterwards
let arr: *mut T = unsafe { std::alloc::alloc(layout) as *mut T };
// alloc is allowed to return a null-pointer if there is not enough memory
if arr.is_null() {
panic!("Out of memory");
}
let first = arr.add(0); // For clarity
// SAFETY: Layout matches `T`
unsafe {
first.write(element); // This 'forgets' the old value
}
self.length += 1;
self.ptr = arr;
self.capacity = INITIAL_CAPACITY;
}Drop for Vec<T> is important
Vec<T> (how?)impl<T> Drop for Vec<T> {
fn drop(&mut self) {
if self.ptr.is_null() {
return;
}
for idx in 0..self.length {
// SAFETY: Element is properly initialized
let element = unsafe { self.ptr.add(index).read() };
drop(element);
}
let layout = Layout::array::<T>(self.capacity)
.expect("Invalid Layout");
// SAFETY: ptr is not null and the Layout matches
unsafe {
dealloc(self.ptr as *const u8, layout);
}
}
}impl<T> Drop for Vec<T> {
fn drop(&mut self) {
if self.ptr.is_null() {
return;
}
// SAFETY: ptr + length elements are properly initialized
// and aligned
unsafe {
let slice: &[T] = std::slice::from_raw_parts_mut(
self.ptr,
self.length,
);
std::ptr::drop_in_place(slice.as_mut_ptr());
}
let layout = Layout::array::<T>(self.capacity)
.expect("Invalid Layout");
// SAFETY: ptr is not null and the Layout matches
unsafe {
dealloc(self.ptr as *const u8, layout);
}
}
}unsafe code to be safe for all possible usage patterns
unsafe code can be made sound by preventing invalid usageVec<T> in the standard library uses tons of unsafe code, but has a safe and easy-to-use APImirimiri tool to detect UB in Rust code!nightly Rustcargo +nightly miri rununsafe code!