Inferred type not working

Hello everyone
I am curious why line with let r3err = ... below gives me compile error and not able to infer the type?
Is there a (more idiomatic/recommended) way of calling .into() to make Rust infer the type by itself?

Thank you very much

use std::convert::Into;
use std::ops::{Add, Range};
fn main() {
    let r: Range<i32> = (MyRange(1i32..1i32) + MyRange(2i32..3i32)).into(); // ok
    let r2: Range<i32> = MyRange::into(MyRange(1i32..1i32) + MyRange(2i32..3i32)); //ok
    let r3ok: Range<i32> = MyRange::<i32>::into(MyRange(1i32..1i32) + MyRange(2i32..3i32)); //ok
    let r3err = MyRange::<i32>::into(MyRange(1i32..1i32) + MyRange(2i32..3i32)); // compile error
    let r0 = MyRange(1i32..1i32) + MyRange(2i32..3i32); // MyRange<i32> custom type
    let r4: Range<i32> = r0.into(); // ok
    println!("range {:#?}", r);
    println!("range {:#?}", r2);
    println!("range {:#?}", r3ok);
    println!("range {:#?}", r4);
}
#[derive(Debug)]
struct MyRange<T: Copy>(std::ops::Range<T>);
impl<T: Add<Output = T> + Copy> Add<MyRange<T>> for MyRange<T> {
    type Output = Self;
    fn add(self, rhs: Self) -> Self {
        MyRange(std::ops::Range {
            start: self.0.start + rhs.0.start,
            end: self.0.end + rhs.0.end,
        })
    }
}
impl<T: Copy> Into<Range<T>> for MyRange<T> {
    fn into(self) -> Range<T> {
        self.0
    }
}

In general, it really helps to paste in the compiler message.

Error [E0282] type annotations needed
I am sorry, I forgot to paste it in original post.
This is on Rust stable (not using the nightly), version 1.57.0, on Windows 10.

These both compile. The compiler not choosing one of them arbitrarily is a good thing.

let r3err: MyRange<_> = MyRange::<i32>::into(MyRange(1i32..1i32) + MyRange(2i32..3i32));
let r3err: Range<_> = MyRange::<i32>::into(MyRange(1i32..1i32) + MyRange(2i32..3i32));

(There are some situations with only one solution where the compiler needs some help to find it, but r3err isn't one of them. I believe it's unsolvable in the most general sense.)

All of your other declarations with annotations (i.e. all except r0) also fail without annotation.

thank you,

so in my code (apart from the one line in error) I did not do anything particularly wrong/bad/poor-practice?

I was wondering if I did something in "poor taste" from the code review viewpoint and I am hoping to learn something from this experience of "intense argument" with the Rust compiler.

Well, it is recommended to implement From instead of Into because implementing the former gives you an implementation of the latter:

impl<T: Copy> From<MyRange<T>> for Range<T> {
    fn from(other: MyRange<T>) -> Self {
        other.0
    }
}

Then you could rewrite the declaration like so (playground):

let r3err = Range::from(MyRange(1i32..1i32) + MyRange(2i32..3i32));

But you could also/still just use an annotation:

let r3err: Range<_> = (MyRange(1i32..1i32) + MyRange(2i32..3i32)).into();

Using an annotation where inference fails, or any place it makes things clearer really, is fine.

2 Likes

thank you for the wonderful (and very quick) explanation!

Yes, in summary, I failed to realize that there were 2 candidate types that Rust could use in my code, hence it (rightly so) complained for me to pick 1 that I wanted.
Incorrectly, I thought there was only 1 type candidate.
Lesson learnt.
Hopefully this thread may help others in future too.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.