Stream: t-compiler/help

Topic: impl for T or for &T deduction


Derek (May 24 2020 at 21:45, on Zulip):

I got confused when I met impl <T> for T and and impy<T> for &T, but it widely used. e.g. in src/libcore/borrow.rs, Here is a simplified example,
playground link!
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3be71dcf0dec08f85e18fbe3de86f6cb

// use std::borrow::Borrow;
pub trait MyBorrow<Borrowed: ?Sized> {
    fn myborrow(&self) -> &Borrowed;
}
impl<T: ?Sized> MyBorrow<T> for T {
    fn myborrow(&self) -> &T {
        println!("T");
        self
    }
}
impl<T: ?Sized> MyBorrow<T> for &T {
    fn myborrow(&self) -> &T {
        println!("ref T"); &**self
    }
}
fn main() {
    let a:i32 = 32;
    let aa = &a;
    let b:&i32 = aa.myborrow();
    println!("b = {:?}", b);
    let aaa = &aa;
    let b:&i32 = aaa.myborrow();
    println!("b = {:?}", b);
}

The output is:

T
b = 32
ref T
b = 32

But aa is also a reference to i32 why it is not use &T version, and aaa is ref to ref i32. it use &T version. that is the deduction rule applied here? Thanks!

lcnr (May 24 2020 at 21:50, on Zulip):

myborrow takes &self.

aa has the type &i32. This means that impl .. for T does not require any auto refs or derefs..
aaa has the type &&i32. impl ... for T does not directly match in this case, as we would first have automatically apply 1 deref (*aa).
For

impl<T: ?Sized> MyBorrow<T> for &T {
    fn myborrow(&self) -> &T {
        println!("ref T"); &**self
    }
}

myborrow actually expected a &&T as an argument, so we have an exact match here

lcnr (May 24 2020 at 21:51, on Zulip):

https://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules may also be helpful here

Derek (May 25 2020 at 22:31, on Zulip):

Thanks, @lcnr
because of auto dereferencing, even without impl<T: ?Sized> MyBorrow<T> for &T. my example here works just fine. and i got:

T
b = 32
T
b = 32

Why we need &T version here, in what situation we need a &T version, in what situation we don't need. Thanks.

lcnr (May 26 2020 at 08:00, on Zulip):

Most of the times you don't need to implement a trait both for T and &(mut) T but there are some cases where it's useful.
For example std::iter::Iterator is implemented for all &mut I where I: Iterator.

fn main() {
    let first = (5..10);
    let mut second = (0..10);

    for (fst, snd) in first.zip(&mut second) {
        // We're using `&mut second` here, because we want to keep using it afterwards.
        println!("fst: {}, snd: {}", fst, snd);
    }

    for rest in second {
        println!("        snd: {}", rest);
    }
}

For a more detailed answer, stackoverflow and https://users.rust-lang.org/ can sometimes be more be helpful.

yunhua (May 27 2020 at 04:42, on Zulip):

Thanks!

Last update: Sep 27 2020 at 14:30UTC