New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confusing Error Message with Impl Trait in E0308 #48290

Open
WalkerCodeRanger opened this Issue Feb 17, 2018 · 1 comment

Comments

Projects
None yet
3 participants
@WalkerCodeRanger
Copy link

WalkerCodeRanger commented Feb 17, 2018

While trying out the new impl trait feature #34511, I wrote some incorrect code. The error message was confusing to me. I've simplified the code and in the process found a very similar error message that is much clearer. My example shows both.

The code (which can be run in the playground on nightly):

#![feature(conservative_impl_trait)]

use std::iter;

fn mask<T: Iterator<Item = i32>>(iter: T) -> impl Iterator<Item = i32> {
    return iter;
}

// this compile error clearly indicated the role of the impl trait
fn test_clear_error_message(condition: bool) -> impl Iterator<Item = i32> {
    if condition {
        mask(iter::once(23))
    } else {
        mask(1..23)
    }
}

// this error message is confusing because the compiler has inferred through
// the impl trait, but gives no indication it has done so
fn test_confusing_error_message(condition: bool) -> impl Iterator<Item = i32> {
    if condition {
        return mask(iter::once(23));
    }
    mask(1..23)
}

fn main() {
    test_clear_error_message(true);
    test_confusing_error_message(true);
}

The first error message is clear. It indicates that impl trait types are involved and that it has figured out what their underlying types are with this nice notation `impl std::iter::Iterator` (struct `std::iter::Once`).

error[E0308]: if and else have incompatible types
  --> src/main.rs:11:5
   |
11 | /     if condition {
12 | |         mask(iter::once(23))
13 | |     } else {
14 | |         mask(1..23)
15 | |     }
   | |_____^ expected struct `std::iter::Once`, found struct `std::ops::Range`
   |
   = note: expected type `impl std::iter::Iterator` (struct `std::iter::Once`)
              found type `impl std::iter::Iterator` (struct `std::ops::Range`)

The second error message is the confusing one. I understand that the compiler has inferred the types and that the types the error message names are not impl trait types. However, it's surprising that the real types are leaking out. I feel like the error message needs to indicate that the inference has passed through the impl trait types. I imagine that something similar could happen in a case where one of the two types was coming from code in a third-party crate. That would be very confusing.

error[E0308]: mismatched types
  --> src/main.rs:24:10
   |
24 |     mask(1..23)
   |          ^^^^^ expected struct `std::iter::Once`, found struct `std::ops::Range`
   |
   = note: expected type `std::iter::Once<i32>`
              found type `std::ops::Range<{integer}>`

Compiler Version

>rustc --version --verbose
rustc 1.25.0-nightly (3ec5a99aa 2018-02-14)
binary: rustc
commit-hash: 3ec5a99aaa0084d97a9e845b34fdf03d1462c475
commit-date: 2018-02-14
host: x86_64-pc-windows-msvc
release: 1.25.0-nightly
LLVM version: 6.0
@Gisleburt

This comment has been minimized.

Copy link

Gisleburt commented Jul 9, 2018

I was about to file a seperate report (glad I checked).

Code to reproduce

use std::time::{SystemTime, UNIX_EPOCH};

fn get_name() -> impl ToString {
    let time = SystemTime::now();
    let timestamp = time.duration_since(UNIX_EPOCH).unwrap().as_secs();
    if  timestamp % 2 == 0 {
        variant1()
    } else {
        variant2()
    }
}

fn variant1() -> impl ToString {
    ", World".to_string()
}

fn variant2() -> impl ToString {
    " World".to_string()
}

fn main() {
    println!("Hello{}!", get_name().to_string());
}

Output

error[E0308]: if and else have incompatible types
  --> src/main.rs:6:5
   |
6  | /     if  timestamp % 2 == 0 {
7  | |         variant1()
8  | |     } else {
9  | |         variant2()
10 | |     }
   | |_____^ expected anonymized type, found a different anonymized type
   |
   = note: expected type `impl std::string::ToString` (anonymized type)
              found type `impl std::string::ToString` (anonymized type)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

The code works if you only return from one function, eg:

fn get_name() -> impl ToString {
    variant1()
}

I get that the compilor probably can't guarantee the same concrete type.

I'd quite like to do a PR to explain that if thats the way to go. I could expand on E0308, or is there a way to explain it in the above output?

Compilor Version

➜ rustc --version --verbose
rustc 1.27.0 (3eda71b00 2018-06-19)
binary: rustc
commit-hash: 3eda71b00ad48d7bf4eef4c443e7f611fd061418
commit-date: 2018-06-19
host: x86_64-apple-darwin
release: 1.27.0
LLVM version: 6.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment