Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Rust compiler not use the expected trait implementation once I add an implementation for Vec<T>?

Tags:

rust

traits

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).

Why can't the compiler find out it should use the Reader<u32> implementation for count initialization? What should I change?

I've found a workaround, but I'm still interested in understanding why the compiler can't figure it out automatically:

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

Issue has been reported as rust-lang/rust#54344.

like image 345
Tey' Avatar asked Sep 03 '18 16:09

Tey'


1 Answers

There's no good reason why the compiler can't figure out that it should use the Reader<u32> implementation for let count: u32 = .... It's a compiler bug because T has no connection to how self.read() is being used on that line. The return type of one .read() call seems to be determining the return type of another .read() call when it shouldn't be!

Additionally, if it were not a bug, then it wouldn't make a difference what implementations of Reader<T> there are other than Reader<u8> and Reader<u32>, but as @rodrigo pointed out, the presence of a Reader<bool> implementation triggers this error.

Note that the ? (which is equivalent to the match shown below) has nothing to do with the bug, since you still get the error when the Result<u32> is gotten directly:

let count_result: Result<u32> = self.read(); // error happens here
let count: u32 = match count_result {
    std::result::Result::Ok(val) => val,
    std::result::Result::Err(err) => {
        return std::result::Result::Err(std::convert::From::from(err))
    }
};
like image 187
Aaron Miller Avatar answered Sep 22 '22 02:09

Aaron Miller