Confused about Infallible associated type when I seem to set it to something else

Hi!

I'm having a bit of an issue with a bit of Rust code regarding traits and associated typed. I've recently been trying to take more advantage of traits, but I've run into an issue which I can't figure out.

I have the following code:

pub struct Block {
    id: String,
    tokens: Vec<Token>,
    current_reader: Box<dyn TokenReader<Error = ParseError>>,
}

impl Block {
    pub fn new() -> Self {
        Self {
            id: "".to_string(),
            tokens: vec![],
            current_reader: Box::new(ArgumentReader::new()),
        }
    }
}

But the issue seems to be with the associated type for the dyn TokenReader<Error = ParseError>. When I try to compile the code I get the following error:

error[E0271]: type mismatch resolving `<ArgumentReader as Reader<char, Token>>::Error == Infallible`
  --> src\mcfunction_file\block.rs:33:29
   |
33 |             current_reader: Box::new(ArgumentReader::new()),
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<ArgumentReader as Reader<char, Token>>::Error == Infallible`
   |
note: expected this to be `Infallible`
  --> src\mcfunction_file\block\readers\argument_reader.rs:16:18
   |
16 |     type Error = ParseError;
   |                  ^^^^^^^^^^
   = note: required for the cast from `readers::argument_reader::ArgumentReader` to the object type `dyn TokenReader<Error = Infallible>`

I'm confused by the fact that it says that there is a mismatch because the Error type is expected to be Infallible, but I thought I set the type to be ParseError.

The TokenReader trait has the following code:

pub trait TokenReader: CharReader<Token> {
    fn new() -> Self
    where
        Self: Sized;
}

The CharReader code looks like this:

pub trait CharReader<R> : Reader<char, R> {
    // TODO look into accepting `impl Into<String>` in some way
    fn read_string(mut self, string: String) -> Result<R, Self::Error>
        where Self: Sized {
        self.read_iter(string.chars().into_iter().collect())
    }
}

And lastly this is the code for the reader trait:

/// A reader accepts incremental inputs until it's done reading.
pub trait Reader<I, R> {
    type Error;

    /// When this returns `true` the reader indicates that the reader is done and [read] should not
    /// be called anymore. If this returns `false` it indicates that the reader is willing to accept
    /// input using the [read] function.
    fn done(&self, input: &I) -> Result<bool, Self::Error>;

    /// Reads and processes the given input.
    fn read(&mut self, input: I) -> Result<(), Self::Error>;

    /// Returns the result of the input and does any needed cleanup.
    fn get_result(self) -> Result<R, Self::Error>;

    // TODO look into accepting `impl Iterator<Item = I>` in some way
    /// Reads an iterator until the reader is done.
    fn read_iter(mut self, iter: Vec<I>) -> Result<R, Self::Error>
        where Self : Sized
    {
        for item in iter {
            if self.done(&item)? {
                break;
            }

            self.read(item)?;
        }

        self.get_result()
    }
}

If anyone could give me some pointers I would really appreciate it.

Also, if you see anything else in terms of code quality or best practices, also let me know. Happy to learn.

Are you sure this is the current definition?

pub struct Block {
    id: String,
    tokens: Vec<Token>,
    current_reader: Box<dyn TokenReader<Error = ParseError>>,
}

E.g. this compiles fine:

But this gives the error you've posted:

+ use core::convert::Infallible;
+ // ...
 pub struct Block {
     id: String,
     tokens: Vec<Token>,
-    current_reader: Box<dyn TokenReader<Error = ParseError>>,
+    current_reader: Box<dyn TokenReader<Error = Infallible>>,
 }

I didn't dive in, but I will note that constructors like new aren't usually parts of traits. You're requiring that implementors can create themselves with no other input ala Default.

So after your comment I decided to take a closer look at my imports and apparently my IDE imported std::string::ParseError which apparently is a type alias for core::convert::Infallible so TIL to double check my imports first. Thanks for taking the time to look into my stupid issue :sweat_smile:

I'll also look into Default, it looks very promising.

Again thx!

Aha! Sneaky, lol.

You don't want Default as a supertrait bound if you want your trait to support dyn Trait. So if you need to pull implementing types out of thin air in a generic context, your fn new() -> Self where Self: Sized is probably the better choice.

It's just not something commonly seen.

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.