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.
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))
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With