I have the following example code:
Rust Playground
use std::marker::PhantomData;
use std::sync::Arc;
struct Field {
dt: T
}
enum T {
Int8,
Int16,
List(Arc<Field>)
}
trait Builder {
fn new() -> Self where Self: Sized;
}
struct Int8Builder {}
impl Builder for Int8Builder {
fn new() -> Self {
Self {}
}
}
struct Int16Builder {}
impl Builder for Int16Builder {
fn new() -> Self {
Self {}
}
}
struct ListBuilder<ValueBuilder: Builder> {
_phantom: PhantomData<ValueBuilder>
}
impl<B: Builder> Builder for ListBuilder<B> {
fn new() -> Self {
Self {
_phantom: PhantomData::default()
}
}
}
I’m trying to have a function that generate the builder by type:
fn get_builder_bad(t: T) -> Box<dyn Builder> {
match t {
T::Int8 => Box::new(Int8Builder::new()),
T::Int16 => Box::new(Int16Builder::new()),
T::List(f) => {
match &f.dt {
T::Int8 => Box::new(ListBuilder::<Int8Builder>::new()),
T::Int16 => Box::new(ListBuilder::<Int16Builder>::new()),
T::List(f) => {
match &f.dt {
T::Int8 => Box::new(ListBuilder::<ListBuilder<Int8Builder>>::new()),
T::Int16 => Box::new(ListBuilder::<ListBuilder<Int16Builder>>::new()),
T::List(f) => {
match &f.dt {
T::Int8 => Box::new(ListBuilder::<ListBuilder<ListBuilder<Int8Builder>>>::new()),
T::Int16 => Box::new(ListBuilder::<ListBuilder<ListBuilder<Int16Builder>>>::new()),
T::List(f) => {
match &f.dt {
T::Int8 => Box::new(ListBuilder::<ListBuilder<ListBuilder<ListBuilder<Int8Builder>>>>::new()),
T::Int16 => Box::new(ListBuilder::<ListBuilder<ListBuilder<ListBuilder<Int16Builder>>>>::new()),
// I want to increase the limit here to be at maximum depth of 20 once I use macro
T::List(f) => unimplemented!("reach the limit of recursion")
}
}
}
}
}
}
}
}
}
}
But I want to convert this to a macro to avoid the obvious duplication
I tried many things including:
macro_rules! nested_list_builder {
// Recursive case for list types
($field:expr, $($start_token:tt)*, $($wrapper_type:ty)* ,$($end_token:tt)*) => {{
match &$field.dt {
T::Int8 => {
type BuilderType = $($wrapper_type $start_token)* Int8Builder $($end_token)*;
Box::new(BuilderType::new())
},
T::Int16 => {
type BuilderType = $($wrapper_type $start_token)* Int16Builder $($end_token)*;
Box::new(BuilderType::new())
},
T::List(f) => {
nested_list_builder!(f , $($start_token)*<, $($wrapper_type)* ListBuilder, >$($end_token)*)
}
}
}};
}
fn get_builder(t: T) -> Box<dyn Builder> {
match t {
T::Int8 => Box::new(Int8Builder::new()),
T::Int16 => Box::new(Int16Builder::new()),
T::List(f) => nested_list_builder!(f, <, ListBuilder, >)
}
}
There are 2 problems:
- this fails with:
error: local ambiguity when calling macro `nested_list_builder`: multiple parsing options: built-in NTs tt ('start_token') or 1 other option.
- It will fail on recursion limit as there is no termination case (I think the solution is to pass X expressions and every call to itself, remove one until we have none and it will match an empty case but I don’t really know how to do it)
I feel like the solution is using Incremental TT munchers or Push-down Accumulation or a combination of both