The ownership story of read
can indeed be a bit confusing; this is because it’s an unsafe operation. It does “copy” the value, but for many non-Copy
types this means that you can very easily create yourself some unsound results, e.g. a double-free if the original value is still also being dropped … or illegal aliasing if the original value is still being used.
Nonetheless, ptr::read
can be quite useful when building some low-level things, especially once you interact with an allocator manually. In such contexts, ptr::read
is usually conceptually just a “move” operation, just that you-the-programmer are in charge for upholding the guarantees of moving, i.e. that the moved-out-of value is no longer used and also no longer dropped. One example is a function such as Vec::pop
, where you can find ptr::read
in the source code. The operation of Vec::pop
logically moves the last element of the Vec
out, and achieves this soundly by updating the len
field accordingly, which the other Vec
methods use to determine which part of the vec is considered initialized, effectively marking that last – now moved-out-of – element as “uninitialized” again.
Comparing to *
operator is a bit of an open-ended question; you’d need to narrow down what exactly to compare to.
Comparing to *
operator on *const T
pointers it probably reasonable … still, the *
operator creates just a “place expression” so what exactly happens then depends a lot on how you actually use it. For maximal analogy to ptr::read
, we can compare to *
-operator being used as a value, i.e. something like
let x = *ptr;
now, this operation will only work for Copy
types, and for those, it’s the same as let x = ptr::read(ptr);
.
Other things the *
operator supports are e.g. taking a reference to the target, e.g. &*ptr
, this is not something the ptr::read
operation can be used for. Or you could create a mutable reference to; or assign to the place, though these are more commonly done with *mut T
pointers. [Note however that as long as the pointer was created with mutable access, a *const T
can be used for mutation, too.] Other things you can do with a place expression: Take a pointer to the target with add_of!
or addr_of_mut!
, or project further to a field of slice index, which gives a new place expression, with which you could in turn do any of the things I’ve listed so far. *
operator usage is indeed quite versatile. With a dereferenced raw pointer, each of these possible operations could have different safety implications; and besides just reading by-value of the whole target (which – still – only actually works for Copy
types), all of these are different from ptr::read
.