Issue using an enum as generic type for a struct

Hi,

I'm implementing a calculator to know the language better but I'm stuck with a strange error.
I plan on using two state machines : one to lex the input and one to parse it. I decided to implement a generic state machine in lib.rs :

pub struct StateMachine<T> {
    pub state: T,
    pub lexeme: String
}

impl<T: Default> StateMachine<T> {
    pub fn new() -> StateMachine<T> {
        StateMachine::<T> {
            state: T::default(),
            lexeme: String::new(),
        }
    }
}

I wanted to use it with two enums : one for the lexer, one for the parser. Here is the lexer one :

#[derive(Debug)]
enum LexerState {
    Beginning,
    Integer,
    PossibleFloat,
    Float,
    Dot,
    Failure,
}

impl Default for LexerState {
    fn default() -> Self {
        LexerState::Beginning
    }
}

I also wrote an enum for the transitions:

#[derive(Debug)]
enum LexerTransition {
    Op,
    Digit,
    Dot,
    WrongSymbol,
}

impl LexerTransition {
    fn from(c: char) -> LexerTransition {
        match c {
            _ if OPERATORS.contains(&c) => LexerTransition::Op,
            n if c.is_digit(10) => LexerTransition::Digit,
            '.' => LexerTransition::Dot,
            _ => LexerTransition::WrongSymbol,
        }
    }
}

My issue comes when I want to implement the state machine using the LexerState enum:

impl<LexerState> StateMachine<LexerState> {
    fn next(self, tr: &LexerTransition) -> LexerState {
        match self.state {
            Beginning => match tr {
                LexerTransition::Digit => LexerState::Integer,
                LexerTransition::Dot => LexerState::Dot,
                _ => LexerState::Failure,
            },
            Dot => match tr {
                LexerTransition::Digit => LexerState::PossibleFloat,
                _ => LexerState::Failure,
            },
            Integer => match tr {
                LexerTransition::Digit => LexerState::Integer,
                LexerTransition::Dot => LexerState::PossibleFloat,
                _ => LexerState::Failure,
            },
            PossibleFloat => match tr {
                LexerTransition::Digit => LexerState::Float,
                _ => LexerState::Failure,
            },
            Float => match tr {
                LexerTransition::Digit => LexerState::Float,
                _ => LexerState::Failure,
            },
            Failure => unreachable!(
                "the lexing loop should have stopped before trying to transition from Failure"
            ),
        }
    }
}

Every single right arm of the matches throws an E0599 error (for example this is the first one):

no associated item named `Integer` found for type parameter `LexerState` in the current scope
associated item not found in `LexerState`rustcE0599

The implementation of StateMachine and the declaration of the enum happen in the same file, so I really am lost here: why is it out of scope ?

Thanks for your help, Ekrips.

You can't access enum variants like that in the pattern matching left arms. You need to access them like:

   LexerState::Integer => match tr {
   ...

I changed that for each of the LexerState left arms, so now I have the exact same issue for them as well. I guess it was an issue waiting to happen, but I'm afraid that's not it.

Mentioning LexerState in impl<...> makes it a type parameter name that shadows the "real" LexerState. Removing it from impl should help:

impl StateMachine<LexerState> {

That did it, thanks! Do you know where I can find more information on this specific topic ?

I don't know off the top of my head. The book has an example of this pattern but doesn't seem to talk about this pitfall: Generic Data Types - The Rust Programming Language

But generally speaking, type parameters can have any name, not just single letter names. Anything mentioned in impl<...> are input parameters for that whole block, not for the type you are implementing for, no matter if it's the same name as something else. Then you can take them and use them in your generic type. So they are temporary placeholders in that block and it's just common that they match the input for the implemented type.

Thanks anyway!

I think I understand now. Thanks a lot!

1 Like

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.