I implemented a small library to make calculations step by step by modifying plan incrementally. I would like to allow making an introspection of the plans without modifying the library itself. For instance I need implementing a function which prints next plan after each step of execution. Or I may need to convert a plan into another representation.
The central abstraction of the library is a Plan<T, R> trait which inputs an
argument T and calculates R:
pub trait Plan<T, R> {
    fn step(self: Box<Self>, arg: T) -> StepResult<R>;
}
Plan returns StepResult<R> which is either an immediate result or new plan
from () to R:
pub enum StepResult<R> {
    Plan(Box<dyn Plan<(), R>>),
    Result(R),
}
Finally I have few specific plans, for example these two:
pub struct OperatorPlan<T, R> {
    operator: Box<dyn FnOnce(T) -> StepResult<R>>,
}
impl<T, R> Plan<T, R> for OperatorPlan<T, R> {
    fn step(self: Box<Self>, arg: T) -> StepResult<R> {
        (self.operator)(arg)
    }
}
pub struct SequencePlan<T1, T2, R> {
    first: Box<dyn Plan<T1, T2>>,
    second: Box<dyn Plan<T2, R>>,
}
impl<T1: 'static, T2: 'static, R: 'static> Plan<T1, R> for SequencePlan<T1, T2, R> {
    fn step(self: Box<Self>, arg: T1) -> StepResult<R> {
        match self.first.step(arg) {
            StepResult::Plan(next) => StepResult::plan(SequencePlan{
                first: next,
                second: self.second,
            }),
            StepResult::Result(result) => StepResult::plan(ApplyPlan{
                arg: result,
                plan: self.second,
            }),
        }
    }
}
Using the plans I can combine operators and calculate R from T step by step
building a plan incrementally.
I have read answers to How do I create a heterogeneous collection of objects? But both "trait" and "enum" solutions doesn't work to me.
I could add new function like fmt or convert into Plan<T, R> trait each
time but the goal is to provide a single function to allow introspection
without modifying the library itself.
I cannot list all plan types as a enum because some of them (like
SequencePlan) are generics and thus OperatorPlan can return a Plan which
exact type is not known in advance.
I tried implementing a Visitor pattern by adding new trait Visitor<T, R> and
method to accept it into Plan<T, R> trait:
pub trait Plan<T, R> {
    fn step(self: Box<Self>, arg: T) -> StepResult<R>;
    fn accept(&self, visitor: &Box<dyn PlanVisitor<T, R>>);
}
trait PlanVisitor<T, R> {
    fn visit_operator(&mut self, plan: &OperatorPlan<T, R>);
    // Compilation error!
    //fn visit_sequence<T2>(&mut self, plan: &SequencePlan<T, T2, R>);
}
This doesn't compile because function visiting SequencePlan is parameterized
by additional type. On the other hand I don't need to know the full type of the
Plan to print it.
In C++ I could use dynamic_cast<Display> to see if Plan is printable and
use the pointer to Display interface after. I know that Rust doesn't support
downcasting out of the box.
I would like to know what is a natural way to implement such introspection in Rust?
More complete code on playground
 
     
    