How to annotate a vector/array of functions?

Hi folks,

I'd like to create a const or static with a vector/array (anything iterable) of MyParser

But I don't know how to specify the type. Could you please help?

pub const MyParsers: [MyParser<_>; 3] = [
    MyParser {
        name: String::from("first"),
        function: Parsed::a,
    },
    MyParser {
        name: String::from("second"),
        function: Parsed::b,
    },
    MyParser {
        name: String::from("third"),
        function: Parsed::c,
    },
];
pub struct MyParser<'a, Y> {
    name: String,
    function: fn(Y) -> Option<Container<'a>>,
}
pub struct Container<'a> {
    inner: &'a str,
}
impl<'a> From<&'a str> for Container<'a> {
    fn from(value: &'a str) -> Self {
        Self { inner: value }
    }
}
pub struct Parsed;
impl Parsed {
    pub fn a<'a, T>(inp: T) -> Option<Container<'a>>
    where
        T: Into<Container<'a>>,
    {
        Some(inp.into())
    }
    pub fn b<'a, T>(inp: T) -> Option<Container<'a>>
    where
        T: Into<Container<'a>>,
    {
        None
    }
    pub fn c<'a, T>(inp: T) -> Option<Container<'a>>
    where
        T: Into<Container<'a>>,
    {
        Some(inp.into())
    }
}

You can't parameterize consts with generics or lifetimes directly on stable. But you can emulate it with associated consts:

pub struct MyParsers<'a, T>(PhantomData<(&'a (), T)>);

impl<'a, T: Into<Container<'a>>> MyParsers<'a, T> {
    pub const ARR: [MyParser<'a, T>; 3] = [
        MyParser {
            name: Cow::Borrowed("first"),
            function: Parsed::a,
        },
        MyParser {
            name: Cow::Borrowed("second"),
            function: Parsed::b,
        },
        MyParser {
            name: Cow::Borrowed("third"),
            function: Parsed::c,
        },
    ];
}

I changed MyParser::name to a Cow because you can't construct a non-empty String during compile time, which is required for consts. So I'm not sure if this will suit your needs or not.

You could also just have a generic function which returns the array instead (without having to use Cow).

1 Like

That's very cool @quinedot , thank you.

That name was just an example, I don't need it to be String so changed it to Enum.

Could you please tell me how do I iterate over the items in the array?

use std::marker::PhantomData;

pub struct MyParsers<'a, T>(PhantomData<(&'a (), T)>);
impl<'a, T: Into<Container<'a>>> MyParsers<'a, T> {
    pub const ARR: [MyParser<'a, T>; 3] = [
        MyParser {
            name: ParserName::First,
            function: Parsed::a,
        },
        MyParser {
            name: ParserName::Second,
            function: Parsed::b,
        },
        MyParser {
            name: ParserName::Third,
            function: Parsed::c,
        },
    ];
}
pub enum ParserName {
    First,
    Second,
    Third,
}
pub struct MyParser<'a, Y> {
    name: ParserName,
    function: fn(Y) -> Option<Container<'a>>,
}
pub struct Container<'a> {
    inner: &'a str,
}
impl<'a> From<&'a str> for Container<'a> {
    fn from(value: &'a str) -> Self {
        Self { inner: value }
    }
}
pub struct MyParsed;
impl MyParsed {
    pub fn a<'a, T>(inp: T) -> Option<Container<'a>>
    where
        T: Into<Container<'a>>,
    {
        Some(inp.into())
    }
    pub fn b<'a, T>(inp: T) -> Option<Container<'a>>
    where
        T: Into<Container<'a>>,
    {
        None
    }
    pub fn c<'a, T>(inp: T) -> Option<Container<'a>>
    where
        T: Into<Container<'a>>,
    {
        Some(inp.into())
    }
    pub fn iterate_over_array() {
        let items = MyParsers.into();
        // ...
    }
}

Something like

    pub fn iterate_over_array<'a, T>(t: T)
    where
        T: Into<Container<'a>> + Clone,
    {
        for item in MyParsers::<'a, T>::ARR {
            let __: Option<Container<'a>> = (item.function)(t.clone());
        }
    }

The generics like T have resolve to concrete "values" (lifetimes/types) before the const can be resolved. In the snippet above, the caller of iterate_over_array will choose what 'a and T are.

(There is no Vec<*> type/higher-kinded type over all possible Vec<T>, only Vec<String>, Vec<()>, etc -- Vec<T> for some concrete T. Similarly, there is no MyParsed::a<*> function or MyParsers::<*>::ARR const.)

1 Like

Thank you, that's very clever :wink: