What's difference between as_deref() and * to a &variable?

today when i code the program, i find the method as_deref() which is so difficult for me to understand. As we all know, we can use " * " to dereference a varibale. So why do we also make the method as_deref()?

as_deref and deref return different types. Option::<T>::as_deref returns Option<&U>, but T::deref returns &U where U is <T as Deref>::Target.

// impl<T> Option<T>
pub fn as_deref(&self) -> Option<&<T as Deref>::Target>
where
    T: Deref,

// deref, the `*` desugar
pub trait Deref {
    type Target: ?Sized;

    // Required method
    fn deref(&self) -> &Self::Target;
}
4 Likes

Let's say we have a type T: Deref<Target = U>. For example T is Arc<bool> and U is bool. Now the type signatures of the operations are important. The * operator, converts T to U, so for example Arc<bool> to bool. The operator is implemented (for not built-in types) via the Deref's trait's deref method which converts &T to &U, so for example &Arc<bool> to &bool.[1]

The as_deref method for Option converts &Option<T> into Option<&U> so for example &Option<Arc<bool>> to Option<&bool>.

So you can already see the main difference is in the type signatures. Option itself doesn’t implement Deref in any way. The type &Option<T> technically does, but that’s just about dereferencing the outer reference.

The as_deref method for Option is a convenience method that combines multiple things. As you can see it turns a value of reference-to-option-of-something into option-of-reference-to-something, which you could view as the first step of what it does. There’s another method, Option::as_ref, that does just that, so &Option<T> to Option<&T>, or for example &Option<Arc<bool>> to Option<&Arc<bool>>. The second step is to dereference the Arc, behind a reference, inside of an Option. This is essentially a mapped application of Deref::deref. So the second step to turn Option<&Arc<bool>> into Option<&bool> would be to call ….map(|x: &Arc<bool>| -> &bool { &**x }, or ….map(|x: &Arc<bool>| -> &bool { Deref::deref(x) }); one could even just write ….map(Deref::deref).

So with x: &Option<T>, you could implement x.as_deref as x.as_ref().map(Deref::deref).

There is also an as_deref method for Result<T, E>. With the typical interpretation that Option<T> is similar to Result<T, E> with some fixed (information-less) error type E, the as_deref method for Result does essentially exactly the same thing as the Option one, so my whole description above translates quite literally to the case of Result, too.

To further illustrate what these methods do for Option, you can try implementing them yourself using match for option_as_ref and option_map and option_as_deref_1, and using the strategy I explained above, of combining option_as_ref and option_map with Deref::deref for option_as_deref_2. E.g. fill out this code

use std::ops::Deref;

fn option_as_ref<T>(x: &Option<T>) -> Option<&T> {
    todo!()
}

fn option_map<A, B>(f: impl FnOnce(A) -> B, y: Option<A>) -> Option<B> {
    todo!()
}

// first alternative: implemented manually
fn option_as_deref_1<T: Deref<Target = U>, U>(x: &Option<T>) -> Option<&U> {
    todo!()
}

// second alternative: implemented by using `option_as_ref` and `option_map`
fn option_as_deref_2<T: Deref<Target = U>, U>(x: &Option<T>) -> Option<&U> {
    todo!()
}

(start working on it in the Playground)


  1. The two conversions T to U or &T to &U can be expressed in terms of each other: The &T to &U conversion done using * operator, starting with x: &T, would be written &**x, dereferencing twice, first to T, then to U and finally taking a reference. The T to U expressed in terms of Deref::deref, and thus the desugaring of the * operator for custom types, starting with t: T would look like *Deref::deref(&t), first taking a reference of type &T, then getting &U from Deref::deref, and finally dereferencing to the U. ↩︎

8 Likes

Thank you :smiley:

appreciate :v: