rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.57k stars 12.74k forks source link

Rust compiler not inferring the correct trait implementation #54344

Open Tey opened 6 years ago

Tey commented 6 years ago

(I've raised a question on SO, and the general answer is that it is due to a bug in rustc that should be reported here, so here I am :) I'm not sure about the report format you expect, so sorry if the following does not match)

I'm trying to implement a reader which could be able to extract values from different types from a file. There is a File struct which represents the file resource (and methods to access its content), and a Reader trait which makes it possible to extract values based on the resulting type. The (dummy) implementation looks like this (playground):

use std::io::Result;

mod file {
    use std::io::Result;

    pub struct File {/* ... */}

    pub trait Reader<T> {
        fn read(&mut self) -> Result<T>;
    }

    impl Reader<u32> for File {
        fn read(&mut self) -> Result<u32> {
            // Dummy implementation
            Ok(10)
        }
    }

    impl Reader<u8> for File {
        fn read(&mut self) -> Result<u8> {
            // Dummy implementation
            Ok(0)
        }
    }

    impl Reader<bool> for File {
        fn read(&mut self) -> Result<bool> {
            // Dummy implementation
            Ok(false)
        }
    }
}

use file::{File, Reader};

impl<T: Default> Reader<Vec<T>> for File
where
    File: Reader<T> + Reader<u32>,
{
    fn read(&mut self) -> Result<Vec<T>> {
        let count: u32 = self.read()?;
        let mut array: Vec<T> = Vec::with_capacity(count as usize);
        for _ in 0..count {
            let mut item: T = self.read()?;
            array.push(item);
        }

        Ok(array)
    }
}

fn main() {
    let mut file = File {};
    let _v: Vec<u8> = file.read().unwrap();
}

Everything worked until I added the Reader<Vec<T>> implementation. Vectors are stored in the file as a u32 indicating the number of elements followed by the element's representation. The compiler gives the following error:

error[E0308]: try expression alternatives have incompatible types
  --> src/main.rs:41:26
   |
41 |         let count: u32 = self.read()?;
   |                          ^^^^^^^^^^^^
   |                          |
   |                          expected u32, found type parameter
   |                          help: try wrapping with a success variant: `Ok(self.read()?)`
   |
   = note: expected type `u32`
              found type `T`

Even though I specified that File implements both Reader<T> and Reader<u32>, it seems to be stuck on Reader<T>.

What's even more strange is that if I only keep 2 implementations of the Reader trait (removing Reader<bool> for instance), the code compiles without any issue (playground).

The current workaround is to explicitly tell the compiler it should use the Reader<u32> implementation:

let count: u32 = (self as &mut Reader<u32>).read()?;

But the compiler should be able to detect this implicitly, as it does when only 2 implementations exist.

Should Rust Playground be trusted, issue appears in stable (1.29.0), unstable (6fdf1dbb9a6d2fbd7894 aka 1.29.0-beta.15) and nightly (2224a42c353636db6ee5 aka 1.30.0-nightly).

Spoonbender commented 2 years ago

Triage: no change