How do I use a type I can't spell out in a type annotation?


#1

This is probably an obvious newbie question. I’m writing a parser for a simple scripting language. I’ve got the tokenizer working; it takes an iterator over Result<char, E>, where E is any Error type, and produces an iterator over Result<Token, TokensError<E>>. Now I’d like to add a convenience method to create a tokenizer from a simple iterator over chars. Here’s an excerpt from my code:

impl<'a, E: Error, C: Iterator<Item=Result<char, E>>> Tokens<'a, C> {
    pub fn new(filename: &'a str, chars: C) -> Tokens<'a, C> {
        Tokens {
            points: Points {
                chars: chars,
                pos: Position {
                    filename: filename,
                    line: 0,
                    column: 0
                }
            }
        }
    }
}

impl<'a, C: Iterator<Item=Result<char, ParseError>>> Tokens<'a, C> {
    pub fn from_chars<H: Iterator<Item=char>>(filename: &'a str, chars: H) -> Tokens<'a, C> {
        Tokens::new(filename, chars.map(|x| -> Result<char, ParseError> { Ok(x) }))
    }
}

When I try to compile it, I get this:

src/tokenizer.rs:68:31: 68:83 error: mismatched types [E0308]
src/tokenizer.rs:68         Tokens::new(filename, chars.map(|x| -> Result<char, ParseError> { Ok(x) }))
                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/tokenizer.rs:68:31: 68:83 help: run `rustc --explain E0308` to see a detailed explanation
src/tokenizer.rs:68:31: 68:83 note: expected type `C`
src/tokenizer.rs:68:31: 68:83 note:    found type `std::iter::Map<H, [closure@src/tokenizer.rs:68:41: 68:82]>`

Okay, it’s clear enough why this isn’t working. But I can’t just write Tokens<'a, std::iter::Map<H, [closure@src/tokenizer.rs:68:41: 68:82]>>. How do I tell the compiler that I want to return the type it looks like I’m returning?


#2

Unfortunately, you can’t return a type which includes a closure right now, because the types are anonymous. There’s ongoing work to provide the ability to return things like impl Iterator<Item=char>, which is a type that implements the trait Iterator, without specifying which type it is. But that feature isn’t available yet.

Instead, you have to use trait objects. You need to wrap your closure in a Box::new, and the type of that boxed closure will be Box<Fn(char) -> Result<char, ParseError>.


#3

Alright. If there isn’t a “good” way to do it, I’ll use a macro—the tokenizer is internal anyway. Thanks!


#4

Actually, in this particular case you should be able to do this:

    pub fn from_chars<H: Iterator<Item=char>>(filename: &'a str, chars: H) -> Tokens<'a, Map<H, fn(char) -> Result<char, ParseError>>> {
        Tokens::new(filename, chars.map(Ok))
    }

You can’t return a closure, but you can return a function, because functions do have a non-anonymous type (written fn(...) -> ...). Type constructors like Ok, Err, Some, and even None all count as functions. Your closure is equivalent to the function Ok, which has the type fn(char) -> Result<char, ParseError>


#5

Thanks again—that works.


#6

yet