use std::fmt::{self, Display}; use std::str::FromStr; trait HandlerOutput {} impl HandlerOutput for () {} impl<S, E> HandlerOutput for Result<S, E> {} trait Handler<Args, Output: HandlerOutput> { type Error: Display; fn call(self) -> Result<Output, Self::Error>; } #[derive(Debug)] struct ConvertFailed; impl Display for ConvertFailed { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(fmt, "Invalid value") } } impl std::error::Error for ConvertFailed {} macro_rules! impl_for_tuple { ($($tys:ident),*) => { impl<F, O, $($tys,)*> Handler<($($tys,)*), O> for F where O: HandlerOutput, F: FnMut($($tys),*) -> O, $($tys: FromStr,)* $(<$tys as FromStr>::Err: Display,)* { type Error = ConvertFailed; fn call(mut self) -> Result<O, Self::Error> { $( #[allow(non_snake_case)] let $tys = match $tys::from_str("0") { Ok(v) => v, Err(_) => return Err(ConvertFailed), }; )* Ok((self)($($tys),*)) } } } } impl_for_tuple!(); impl_for_tuple!(T0); impl_for_tuple!(T0, T1); impl_for_tuple!(T0, T1, T2); fn run<H, A, O>(h: H) -> Result<O, H::Error> where O: HandlerOutput, H: Handler<A, O>, { h.call() } fn main() -> Result<(), Box<dyn std::error::Error>> { let mut x: isize = -1; let mut y: isize = -1; let mut xs: (u8, u8) = (0, 0); run(|| x = 1)?; run(|a, b| xs = (a, b))?; //run(|a| /*-> Result<(), std::num::ParseIntError> */ { y = str::parse(a)?; Ok(()) })?; //run(|a: usize| Ok::<(), ()>(())?); println!("{} {}", x, y); Ok(()) }