ian.c
June 7, 2025, 5:45am
1
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 const
s with generics or lifetimes directly on stable. But you can emulate it with associated const
s:
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 const
s. 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
ian.c
June 7, 2025, 8:20am
4
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
ian.c
June 9, 2025, 7:10am
6
Thank you, that's very clever