Interruptible Implementation

No Interruptibility

fn execute(
    params: Params,
) -> Outcome {
    let output_1 = step_1(params);
    let output_2 = step_2(output_1);
    let outcome = step_3(output_2);

    return outcome;
}

Basic

fn execute(
    interrupt_rx: mut Receiver<InterruptSignal>,
    params: Params,
) -> ControlFlow<InterruptSignal, Outcome> {
    let () = interruptibility_check(&mut interrupt_rx)?;
    let output_1 = step_1(params);

    let () = interruptibility_check(&mut interrupt_rx)?;
    let output_2 = step_2(output_1);

    let () = interruptibility_check(&mut interrupt_rx)?;
    let outcome = step_3(output_2);

    ControlFlow::Continue(outcome)
}

fn interruptibility_check(receiver: &mut Receiver<InterruptSignal>)
-> ControlFlow<InterruptSignal, ()> {
    if let Ok(interrupt_signal) = interrupt_rx.try_recv() {
        ControlFlow::Continue(())
    } else {
        ControlFlow::Break(interrupt_signal)
    }
}

"Better"

fn execute<T>(
    interrupt_rx: mut Receiver<InterruptSignal>,
    params: Params,
) -> Result<T, InterruptSignal> {
    [step_1, step_2, step_3]
        .into_iter()
        .try_fold(params, |(mut last_param, step)| {
            interruptibility_check(&mut interrupt_rx)?;
            step(last_param)
        })
}

Real Code

Playground

use std::any::Any;
use std::iter::IntoIterator;
use std::marker::PhantomData;

fn main() {
    match execute::<String, _>(Receiver::new(3), true) {
        Ok(value) => println!("main: {}", value),
        Err(InterruptSignal) => println!("main: interrupted!"),
    }
}

fn execute<T, I>(
    mut interrupt_rx: Receiver<InterruptSignal>,
    params: I,
) -> Result<T, InterruptSignal>
where
    T: 'static,
    I: 'static,
{
    [step(step_1), step(step_2), step(step_3)]
        .into_iter()
        .try_fold(Box::new(params) as Box<dyn Any>, |last_param, step| {
            interruptibility_check(&mut interrupt_rx)?;
            Ok(step.exec(last_param))
        })
        .map(|boxed_any| *boxed_any.downcast().unwrap())
}

fn step_1(takes_bool: bool) -> u32 {
    let number = 123;
    println!("step_1 ({takes_bool}: bool) -> {number}");
    number
}

fn step_2(takes_u32: u32) -> f64 {
    let float = 1.5;
    println!("step_2 ({takes_u32}: u32) -> {float}");
    float
}

fn step_3(takes_f64: f64) -> String {
    let string = String::from("magic");
    println!("step_3 ({takes_f64}: f64) -> {string}");
    string
}

fn step<F, I, O>(f: F) -> Box<dyn StepErased>
where
    F: Fn(I) -> O + 'static,
    I: 'static,
    O: 'static,
{
    Box::new(StepWrapper::new(f))
}

trait Step {
    type Input: Any;
    type Output: Any;
    fn exec(&self, input: Self::Input) -> Self::Output;
}

impl<F, I, O> Step for StepWrapper<F, I, O>
where
    F: Fn(I) -> O,
    I: 'static,
    O: 'static,
{
    type Input = I;
    type Output = O;

    fn exec(&self, input: Self::Input) -> Self::Output {
        (self.0)(input)
    }
}

trait StepErased {
    fn exec(&self, input: Box<dyn Any>) -> Box<dyn Any>;
}

struct StepWrapper<F, I, O>(F, PhantomData<(I, O)>);
impl<F, I, O> StepWrapper<F, I, O> {
    fn new(f: F) -> Self {
        Self(f, PhantomData)
    }
}

impl<F, I, O> StepErased for StepWrapper<F, I, O>
where
    StepWrapper<F, I, O>: Step<Input = I, Output = O>,
    I: 'static,
    O: 'static,
{
    fn exec(&self, input: Box<dyn Any>) -> Box<dyn Any> {
        let input = *input.downcast::<I>().unwrap();
        let output = Step::exec(self, input);
        Box::new(output)
    }
}

struct Receiver<T>(u32, PhantomData<T>);
impl<T> Receiver<T> {
    pub fn new(n: u32) -> Self {
        Self(n, PhantomData)
    }
}
#[derive(Debug)]
struct InterruptSignal;
fn interruptibility_check(rx: &mut Receiver<InterruptSignal>) -> Result<(), InterruptSignal> {
    if (rx.0) == 0 {
        Err(InterruptSignal)
    } else {
        rx.0 -= 1;
        Result::Ok(())
    }
}