By HanDong Zhang
Understanding by Module
In fact, the classification by standard library will first give you a small idea of what they do.
-
std::ops::Deref . As you can see,
Derefis categorized as anopsmodule. If you look at the documentation, you will see that this module defines the trait fr all the overloadable operators. For example,Add traitcorresponds to+, whileDeref traitcorresponds to a shared(immutable) borrowing dereference operation, such as*v. Correspondingly, there is also theDerefMut trait, which corresponds to the dereferencing operation of exclusive(mutable) borrowing. Since the Rust ownership semantics is a language feature throughout , the semantics ofOwner/immutable borrowing(&T)/mutable borrowing(&mut T)all appear together. -
std::convert::AsRef . As you can see,
AsRefis grouped under the convert module. if you look at the documentation, you will see that traits related to type conversions are defined in this module. For example, the familiar "From/To", "TryFrom/TryTo" and "AsRef/AsMut" also appear in pairs here, indicating that the feature is releated to type conversions. Based on the naming rules in the Rust API Guidelines , wen can infer that methods starting withas_represent conversions fromborrow -> borrow, i.e,reference -> reference, and are overhead-free. And such conversions do not fail. -
std::borrow::Borrow. As you can see,
Borrowis categorized in the borrow module. The documentation for this module is very minimal, with a single sentence saying that this is for using borrowed data. So the trait is more or less related to expressing borrwo semantics. Three traits are provided: Borrow / BorrowMut/ ToOwned , which corresponds exactly to the ownership semantics. -
std::borrow::Cow. It can be seen that
Cowis also classified as a borrow module. According to the description,Cowis a clone-on-write smart pointer. The main reason for putting it in the borrow module is to use borrowing as much as possible and avoid copying, as an optimization.
std::ops::Deref
First, let's look at the definition of the trait.
pub trait Deref {
type Target: ?Sized;
#[must_use]
pub fn deref(&self) -> &Self::Target;
}
The definition is not complicated, Deref contains only a deref method signature. The beauty of this trait is that it is called "implicitly" by the compiler, officially called "deref coercion ".
Here is an example from the standard library.
use std::ops::Deref;
struct DerefExample<T> {
value: T
}
impl<T> Deref for DerefExample<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
let x = DerefExample { value: 'a' };
assert_eq!('a', *x);
In the code, the DerefExample structure implements the Deref trait, so it can be executed using the dereference operator *. In the example, the value of the field value is returned directly.
As you can see, DerefExample has a pointer-like behavior , because it implements Deref, because it can be dereferenced. DerefExample also becomes a kind of smart pointer. This is one way to identify if a type is a smart pointer, by seeing if it implements Deref. But not all smart pointers implement Deref, some implent Drop, or both.
Now let's summarize Deref.
If T implements Deref<Target=U>, and x is an instance of type T, then.
- In an immutable context, the operation of
*x(whenTis neither a reference nor a primitive pointer) is equivalent to*Deref::deref(&x). - The value of
&Tis forced to be converted to the value of&U. (deref coercion). -
Timplements all the (immutable) methods ofU.
The beauty of Deref is that it enhances the Rust development experience. A typical example from the standard library is that Vec<T> shares all the methods of slice by implemented Deref.
impl<T, A: Allocator> ops::Deref for Vec<T, A> {
type Target = [
