What's the purpose of std::ptr::read?

My mental model for raw pointers (putting all the restrictions that come with using them safely aside for a moment) is that dereferencing one (*) is similar to using a reference, std::ptr::write is moving a value into the referenced memory, std::ptr::read is moving a value out of the referenced memory to the caller. The purpose of std::ptr::write is quite clear: it's not safe to write to non-initialized memory using pointer dereference (uninitialized value should not be dropped). It's not clear to me how std::ptr::read differences from dereferencing the pointer however and if there's more to it than semantic difference (after read the memory has to be assumed uninitialized)?

You can't move non-Copy objects using just *. For example, here's Vec::pop:

    pub fn pop(&mut self) -> Option<T> {
        if self.len == 0 {
            None
        } else {
            unsafe {
                self.len -= 1;
                Some(ptr::read(self.get_unchecked(self.len())))
            }
        }
    }

You couldn't have implemented it with just * :slightly_smiling_face:

2 Likes

Yeah, basically the thing is that ptr::read will leave a copy of the thing you read at the location of the pointer. Another example of using it can be found here.

1 Like

Thank you @krdln and @alice! After playing with your examples I understand the difference now. Dereferencing a raw pointer is like accessing a reference: the referenced value can be read but not moved (except if it's Copy). It doesn't allow one to sidestep borrow checking: &T -> *const T -> T is not possible. std::ptr::read allows to go from *const T to T (exactly as it's signature states), basically stating to the compiler "trust me, we're moving it".

Using pop as an illustration:

fn pop<T>(v: &mut Vec<T>) -> Option<T> {
    if v.len() == 0 {
        None
    } else {
        // FIXME: update length of the vector.
        unsafe { Some(std::ptr::read(v.get_unchecked(v.len()))) }
        // error[E0507]: cannot move out of a raw pointer
        // unsafe { Some(*(v.get_unchecked(v.len()) as *const T)) }
    }
}