Auto dereferencing of &u32 for TryInto<usize> for u32

Hello,

I have a confusion on auto dereferencing.
Here's some code that confuses me
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f5a03fb44c912c62b25390084114dfab

use std::convert::TryInto;

fn main()
{
    let x = 5u32;
    let x_ref = &x;
    let conversion: usize = x_ref.try_into().unwrap(); // bad
    // let conversion: usize = (*x_ref).try_into().unwrap(); // good

    println!("{}", conversion);
}

Specifically, I'm confused why:

x_ref.try_into()

doesn't work.

After reading https://doc.rust-lang.org/stable/reference/expressions/method-call-expr.html I thought Rust generates the following candidate receiver types for checking try_into():

&u32, &&u32, &mut &u32, u32

and that the last candidate would match - but I get an error I'm confused about.

Can someone explain where my understanding goes wrong?

This TryFrom impl satisfies this blanket impl, so u32: TryInto<usize>.

But, TryInto::try_into is declared as so:

fn try_into(self) -> Result<T, Self::Error>;

Since Self is u32, it cannot work with &u32.

In a very abstract way, you can imagine &u32 to translate to something like SharedReference<u32>. The primary type of what you're trying to convert is a SharedReference and u32 is only the type parameter and not the other way around. The standard library doesn't implement TryInto for shared references, because it'd be ambiguous. If you try to convert a shared reference of a u32 into a usize, it could either dereference and convert the u32 into a usize or it could try to convert the reference itself into a usize, which would correspond to the address of where the u32 is stored.

&u32, &&u32, &mut &u32, u32

I think there are too many levels of indirection here. As OptimisticPeach said, try_into is only implemented via the blanket impl:

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 

So there is no direct impl for u32 here - it has to go through this generic thing first. Because of that generic impl, I believe it won't auto-deref, instead failing to recognize a method.

This. To "prove" it, the following code compiles:

trait MyTryInto<Dst> {
    fn try_into (self) -> Option<Dst>
    ;
}

impl MyTryInto<usize> for u32 {
    fn try_into (self: u32) -> Option<usize>
    {
        ::core::convert::TryFrom::try_from(self).ok()
    }
}

fn main ()
{
    let x: &u32 = &42;
    let _: usize = x.try_into().unwrap();
}
  • Playground

  • Here Rust has no choice but to auto-deref x to be able to find our .try_into() method. Whereas with ::core::convert::TryInto's .try_into(), it thinks it can use &'_ u32's .try_into() (so it does not auto-deref), but the only existing &'_ u32's .try_into() impl is one that delegates to TryFrom<&'_ u32>, for which there are no impls and thus fails.