Rust generic trait implementations, depending on associated type

45 views Asked by At

I'm currently writing a parsing library that should allow for generic parsing of, for example, binary files, a series of tokens, or a character string. Therefore I defined two traits:

trait Supplier {
    type Item: Clone;
    type Error: Debug;

    /// get the current item, without advancing
    fn current(&mut self) -> Result<Option<Self::Item>, Self::Error>;

    /// step the supplier, either forward or backwards
    fn step(&mut self, steps: i64) -> Result<(), Self::Error>;

    // … more functions with default implementations
}

trait Parseable<Item> {
    type Output;

    /// parse Self from a given [`Supplier`]
    fn parse<S: Supplier<Item = Item>>(supplier: &mut S) -> Result<Self::Output, S::Error>;
}

A type that can supplier some items implements the Supplier trait. For example, a binary file would implement Supplier<Item = u8>, a text-file Supplier<Item = char> and a tokenizer/scanner/lexer Supplier<Item = Token>.
A type that can be parsed from a stream of items implements Parseable<Item> with Item being the type of items in the stream it can be parsed from. For example, a usize would implement Parseable<u8> and an expression (when thinking about a programming language or maths expression) implements Parseable<Token> for some type Token.

I need the Parseable::Output to be an associated type in order to allow types to fail parsing using a Result<Self, /* some error type */> as output.

I then want to write some (generic) implementations for existing types, one of which is a Vec<T>. Here the following problem arises: In case T::Output is a Result<T, /* some error type */, I want to return a Result<Vec<T>, T::Error> (the error of course in case one parse of T fails). In the case that T::Output is T itself, i.e. its parse is infallible, the return type should be Vec<T> directly.
I hope it's clear what I want to do.

I've tried the following set of implementations:

impl<Item, T, Error> Parseable<Item> for Vec<T>
where
    T: Parseable<Item, Output = Result<T, Error>>,
    usize: Parseable<Item>,
{
    type Output = Result<Self, Error>;

    fn parse<…>(…) -> … { … }
}

impl<Item, T> Parseable<Item> for Vec<T>
where
    T: Parseable<Item, Output = T>,
    usize: Parseable<Item>,
{
    type Output = Self;

    fn parse<…>(…) -> … { … }
}

The compiler shows an error E0119 (conflicting implementations of trait Parseable<_> for type Vec<_>). I kind of see why the compiler sees two conflicting implementations, but don't quite understand why exactly they are conflicting. And, in case it exists, what is the correct / idiomatic way of implementing this?

1

There are 1 answers

0
cafce25 On

Associated types are designed to allow only one implementation. So you don't want an associated type, you want a plain generic.

trait Parseable<Item, Output> {
    /// parse Self from a given [`Supplier`]
    fn parse<S: Supplier<Item = Item>>(supplier: &mut S) -> Result<Output, S::Error>;
}

impl<Item, T, Error> Parseable<Item, Result<Self, Error>> for Vec<T>
where
    T: Parseable<Item, Output = Result<T, Error>>,
    usize: Parseable<Item>,
{
    fn parse<…>(…) -> … { … }
}

impl<Item, T> Parseable<Item, Self> for Vec<T>
where
    T: Parseable<Item, Output = T>,
    usize: Parseable<Item>,
{
    fn parse<…>(…) -> … { … }
}