So I sort of figured this out while writing this up, but heck!
I got this far maybe this will help someone elseI also could be wrong still. EDIT: I was wrong
I'm pretty sure my mistake was thinking I could return a struct that is generic over some type
I: Iterator<Item = Thing>
by giving it one concrete type that satisfied that bound.
EDIT: See the solution for how to do this
The problem was I made that type inside the function body. The function body which promised to be generic overI
in its signature... (because I had to get anI
from somewhere right?) Woops!I think what threw me was that the error only said the types didn't match... which is true but doesn't highlight the bigger problem of
"You claim to be generic over {Generic Type} but you are using {concrete type}. No concrete type is going to match {Generic Type}. Help: Introduce a parameter of type {Generic Type}"
Maybe there's a diagnostic improvement to be made here?I also made a minimal example, here: Rust Playground , during which I realised my mistake!
EDIT: Quinedot's Rust Playground is much better though!
I have a somewhat complicated concrete Iterator type*, but it can be very simply described as a generic Iterator: Iterator<Item = Token>
. Here's my method that tries to return a struct holding that generic iterator in a field:
pub fn parse_tokens<I>(&self) -> Parser<I>
where
I: Iterator<Item = Token>,
{
let token_stream: Peekaboo<I> = self.get_token_stream().filter_map(/* elided for brevity */).peekaboo();
Parser::new(token_stream)
}
Peekaboo is an Iterator I wrote for peeking up to twice (which was really fun!).
The above method (of a struct that isn't Parser
) fails to compile, the error message confuses me though:
mismatched types
expected struct `Peekaboo<I>`
found struct `Peekaboo<FilterMap<TokenStream<'_>, fn(Result<Token, Error>) -> Option<Token> {token_filter_map}>>`rustcE0308
lexer.rs(29, 25): this type parameter
lexer.rs(33, 21): expected due to this
It wants specifically the Generic type, but it found a concrete type.
How have I run into this problem? I feel like I've passed concrete types to generic functions all the time.
What do I even do to make Rust "find" Peekaboo<I>
? Why isn't a concrete type satisfying the generic sufficient here? I can't build a Generic type can I? Something doesn't feel right.
This case is a bit odd (I probably need to sleep on it and make some better architectural decisions )
because the type being returned is a generic struct:
pub struct Parser<I>
where
I: Iterator<Item = Token>,
{
tokens: Peekaboo<I>, // this lets me peek up to 2 Tokens ahead
current: usize,
}
Peekaboo is generic over Iterators as you might expect (this is in a different crate):
pub struct Peekaboo<I: Iterator> {
iter: I,
// ...
}
I can use token_stream in more "normal" or simple situations with the same I
.
This compiles fine:
pub fn parse_tokens<I>(&'source self) -> Parser<I>
where
I: Iterator<Item = Token> + 'source,
{
let tokens = self.scan_tokens().filter_map(token_filter_map).peekaboo();
wants_token_iter(tokens);
todo!();
// Parser::new(tokens)
}
}
fn wants_token_iter<I>(_want: Peekaboo<I>)
where
I: Iterator<Item = Token>,
{
}
Amusingly enough, so this compiles fine too:
let tokens: Peekaboo<I> = (|| todo!())();
Parser::new(tokens)
So maybe there is some way to make this work. That's still on my todo list!
Everything else Parser
side compiles fine with this in place.
EDIT: post solution me here! Post "Epiphany me is going to talk some nonsense! Now I would advise "
impl Trait
in return position"Post epiphany me again. Pretty sure there isn't a way to make this work without introducing a parameter to
parse_tokens
of typeI
. So I wouldn't advise actually using this!