diff --git a/CHANGELOG.md b/CHANGELOG.md index 8427ac4..d722b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Backwards-incompatible changes - `typle_bounds!` has been replaced by support for `typle!` macro in where clauses. +- `typle_args` as a synonym for `typle!` has been removed. # 0.11 diff --git a/Cargo.toml b/Cargo.toml index 6c76104..c80282a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ proc-macro = true either = "1" proc-macro2 = "1" quote = "1" -syn = { version = "2.0.43", features = ["full", "extra-traits"] } +syn = { version = "2.0.43", features = ["full", "parsing"] } zip_clone = "0.1.0" [dev-dependencies] diff --git a/README.md b/README.md index 196b725..7449924 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,13 @@ For example, to define a function to zip a pair of tuples into a tuple of pairs: pub fn zip( a: A, b: B, -) -> (typle![i in .. => (A<{i}>, B<{i}>)]) +) -> (typle! { + i in .. => (A<{i}>, B<{i}>) +}) { - (typle!{i in .. => (a[[i]], b[[i]])}) + (typle! { + i in .. => (a[[i]], b[[i]]) + }) } ``` @@ -61,7 +65,7 @@ where T: Tuple, // `T`` is a tuple with 0 to 3 components. T<_>: HandleStuff, // All components implement `HandleStuff`. { - type Output = (typle!(i in .. => T<{i}>::Output)); + type Output = (typle! {i in .. => T<{i}>::Output}); // Return a tuple of output from each handler applied to the same input. fn handle_stuff(&self, input: Input) -> Self::Output { @@ -69,7 +73,9 @@ where () } else { ( - typle!(i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone())), + typle! { + i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) + }, // Avoid expensive clone for the last handler. self.handlers[[T::LAST]].handle_stuff(input), ) @@ -90,7 +96,7 @@ impl HandleStuff for MultipleHandlers<(T0,)> where T0: HandleStuff, { - type Output = (::Output,); + type Output = (T0::Output,); fn handle_stuff(&self, input: Input) -> Self::Output { { (self.handlers.0.handle_stuff(input),) } } @@ -100,7 +106,7 @@ where T0: HandleStuff, T1: HandleStuff, { - type Output = (::Output, ::Output); + type Output = (T0::Output, T1::Output); fn handle_stuff(&self, input: Input) -> Self::Output { { ( @@ -116,7 +122,7 @@ where T1: HandleStuff, T2: HandleStuff, { - type Output = (::Output, ::Output, ::Output); + type Output = (T0::Output, T1::Output, T2::Output); fn handle_stuff(&self, input: Input) -> Self::Output { { ( diff --git a/src/context.rs b/src/context.rs index eb666fa..2fec6a2 100644 --- a/src/context.rs +++ b/src/context.rs @@ -594,14 +594,22 @@ impl TypleContext { } let mut bounded_ty = syn::parse2::(bounded.into_iter().collect())?; context.replace_type(&mut bounded_ty)?; - let mut bound = syn::parse2::(bound.into_iter().collect())?; - context.replace_bound(&mut bound)?; - Ok(WherePredicate::Type(syn::PredicateType { - lifetimes: None, - bounded_ty, - colon_token: token::Colon::default(), - bounds: std::iter::once(bound).collect(), - })) + let bounds = Punctuated::::parse_terminated + .parse2(bound.into_iter().collect())? + .into_iter() + .map(|mut bound| { + context.replace_bound(&mut bound)?; + Ok(bound) + }) + .collect::>()?; + Ok(Replacements::>::Singleton(Ok( + WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty, + colon_token: token::Colon::default(), + bounds, + }), + ))) }) } diff --git a/src/context/expr.rs b/src/context/expr.rs index 9746a08..0605ac0 100644 --- a/src/context/expr.rs +++ b/src/context/expr.rs @@ -318,15 +318,11 @@ impl TypleContext { // t[[0]] let mut iter = array.elems.iter_mut().fuse(); let (Some(field), None) = (iter.next(), iter.next()) else { - abort!(index.index, "unsupported tuple index1"); + abort!(index.index, "unsupported tuple index"); }; self.replace_expr(field, state)?; - let deb = field.clone(); let Some(i) = evaluate_usize(field) else { - abort!( - index.index, - format!("unsupported tuple index {:?} {:?}", self.constants, deb) - ); + abort!(index.index, format!("unsupported tuple index")); }; *expr = Expr::Field(syn::ExprField { attrs: std::mem::take(&mut index.attrs), @@ -575,14 +571,23 @@ impl TypleContext { match &mut expr { Expr::Macro(syn::ExprMacro { mac, .. }) => { if let Some(ident) = mac.path.get_ident() { - if ident == "typle" || ident == "typle_args" { + if ident == "typle" { let token_stream = std::mem::take(&mut mac.tokens); return self .expand_typle_macro(token_stream, |context, token_stream| { - let mut expr = syn::parse2::(token_stream)?; - let mut state = BlockState::default(); - context.replace_expr(&mut expr, &mut state)?; - Ok(expr) + Ok(Replacements::Iterator( + Punctuated::::parse_terminated + .parse2(token_stream)? + .into_iter() + .map(|mut expr| { + let mut state = BlockState::default(); + context.replace_expr(&mut expr, &mut state)?; + Ok(expr) + }) + // collect and into_iter due to context lifetime + .collect::>() + .into_iter(), + )) }) .map_iterator(Either::Left); } diff --git a/src/context/pat.rs b/src/context/pat.rs index 03fe74b..2cddc19 100644 --- a/src/context/pat.rs +++ b/src/context/pat.rs @@ -119,13 +119,13 @@ impl TypleContext { match &mut pat { Pat::Macro(syn::PatMacro { mac, .. }) => { if let Some(ident) = mac.path.get_ident() { - if ident == "typle" || ident == "typle_args" { + if ident == "typle" { let token_stream = std::mem::take(&mut mac.tokens); return self.expand_typle_macro(token_stream, |context, token_stream| { let mut pat = Pat::parse_single.parse2(token_stream)?; let mut state = BlockState::default(); context.replace_pat(&mut pat, &mut state)?; - Ok(pat) + Ok(Replacements::>::Singleton(Ok(pat))) }); } } diff --git a/src/context/shared.rs b/src/context/shared.rs index 10185ee..64c7f7d 100644 --- a/src/context/shared.rs +++ b/src/context/shared.rs @@ -448,14 +448,15 @@ impl TypleContext { Ok(None) } - pub(super) fn expand_typle_macro( + pub(super) fn expand_typle_macro( &self, token_stream: TokenStream, f: F, ) -> Replacements>> where T: 'static, - F: Fn(&TypleContext, TokenStream) -> syn::Result + 'static, + F: Fn(&TypleContext, TokenStream) -> syn::Result>, + I: Iterator>, { let mut tokens = token_stream.into_iter(); let (pattern, range) = match self.parse_pattern_range(&mut tokens) { @@ -472,17 +473,20 @@ impl TypleContext { if let Some(ident) = pattern.clone() { context.constants.insert(ident, 0); } - return Replacements::Iterator(range.zip_clone(token_stream).flat_map( + Replacements::Iterator(range.zip_clone(token_stream).flat_map( move |(index, token_stream)| { if let Some(ident) = &pattern { *context.constants.get_mut(ident).unwrap() = index; } match context.evaluate_if(token_stream) { - Ok(Some(token_stream)) => Some(f(&context, token_stream)), - Ok(None) => None, - Err(e) => Some(Err(e)), + Ok(Some(token_stream)) => match f(&context, token_stream) { + Ok(iter) => iter, + Err(err) => Replacements::Singleton(Err(err)), + }, + Ok(None) => Replacements::Empty, + Err(err) => Replacements::Singleton(Err(err)), } }, - )); + )) } } diff --git a/src/context/type.rs b/src/context/type.rs index cba5688..2f09e48 100644 --- a/src/context/type.rs +++ b/src/context/type.rs @@ -91,13 +91,22 @@ impl TypleContext { match &mut ty { Type::Macro(syn::TypeMacro { mac }) => { if let Some(ident) = mac.path.get_ident() { - if ident == "typle" || ident == "typle_args" { + if ident == "typle" { let token_stream = std::mem::take(&mut mac.tokens); return self .expand_typle_macro(token_stream, |context, token_stream| { - let mut ty = syn::parse2::(token_stream)?; - context.replace_type(&mut ty)?; - Ok(ty) + Ok(Replacements::Iterator( + Punctuated::::parse_terminated + .parse2(token_stream)? + .into_iter() + .map(|mut ty| { + context.replace_type(&mut ty)?; + Ok(ty) + }) + // collect and into_iter due to context lifetime + .collect::>() + .into_iter(), + )) }) .map_iterator(Either::Left); } diff --git a/src/lib.rs b/src/lib.rs index a525a5b..2353836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,9 @@ +//! Note, for best results using `typle` with rust-analyzer, ensure that proc macro attributes are enabled: +//! ```json +//! rust-analyzer.procMacro.enable: true, +//! rust-analyzer.procMacro.attributes.enable: true +//! ``` +//! //! # `#[typle(...)]` //! The `typle` attribute macro generates code for multiple tuple lengths. This code: //! @@ -57,39 +63,51 @@ //! assert_eq!(split_first((3.0,)), (3.0, ())); //! ``` //! -//! # `typle!(...)` +//! The associated constant `LEN` provides the length of the tuple in each +//! generated item. This value can be used in typle index expressions. +//! The default bounds for a macro range are `0..Tuple::LEN`, that is, for all +//! components of the tuple. +//! +//! ```rust +//! # use typle::typle; +//! #[typle(Tuple for 0..12)] +//! fn append(t: T, a: A) -> (T<{..}>, A) { +//! (t[[..]], a) +//! } +//! +//! assert_eq!(append((1, 2, 3), 4), (1, 2, 3, 4)); +//! ``` +//! +//! # `typle!` //! //! The `typle!` macro creates a new sequence of types or expressions. A //! `typle!` macro can only appear where a comma-separated sequence is valid //! (e.g. a tuple, array, argument list, or where clause). //! -//! The associated constant `LEN` provides the length of the tuple in each -//! generated item. This value can be used in typle index expressions. -//! -//! ``` +//! ```rust //! # use typle::typle; //! #[typle(Tuple for 0..=12)] -//! fn reverse(t: T) -> (typle!(i in 1..=T::LEN => T<{T::LEN - i}>)) { -//! (typle!(i in 1..=T::LEN => t[[T::LEN - i]])) +//! fn reverse(t: T) -> (typle! {i in 1..=T::LEN => T<{T::LEN - i}>}) { +//! (typle! {i in 1..=T::LEN => t[[T::LEN - i]]}) //! } //! //! assert_eq!(reverse((Some(3), "four", 5)), (5, "four", Some(3))); //! ``` //! -//! For types, `T<{start..end}>` is a shorthand for `typle!(i in start..end => T<{i}>)`. -//! For expressions, `t[[start..end]]` is a shorthand for `typle!(i in start..end => t[[i]])`. -//! -//! The default bounds for a macro range are `0..Tuple::LEN`, that is, for all -//! components of the tuple. -//! -//! ``` +//! Each iteration can add multiple components to the new structure. +//! ```rust //! # use typle::typle; -//! #[typle(Tuple for 0..12)] -//! fn append(t: T, a: A) -> (T<{..}>, A) { -//! (t[[..]], a) +//! #[typle(Tuple for 0..=12)] +//! fn duplicate_components( +//! t: T, +//! ) -> (typle! {i in .. => T<{i}>, T<{i}>}) +//! where +//! T<_>: Clone, +//! { +//! (typle! {i in .. => t[[i]].clone(), t[[i]]}) //! } //! -//! assert_eq!(append((1, 2, 3), 4), (1, 2, 3, 4)); +//! assert_eq!(duplicate_components(("one", 2, 3.0)), ("one", "one", 2, 2, 3.0, 3.0)); //! ``` //! //! # Constraints @@ -98,9 +116,9 @@ //! forms. Except for the first form, these constraints can only appear in the //! `where` clause. //! - `T: Tuple` - all components of the tuple have type `C`. -//! - `T<_>: Copy` - all components of the tuple implement the `Copy` trait. -//! - `T<0>: Copy` - the first component of the tuple implements the `Copy` trait. -//! - `T<{1..=2}>: Copy` - the second and third components implement the `Copy` trait. +//! - `T<_>: Clone` - all components of the tuple implement the `Clone` trait. +//! - `T<0>: Clone` - the first component of the tuple implements the `Clone` trait. +//! - `T<{1..=2}>: Clone` - the second and third components implement the `Clone` trait. //! - `typle!(j in .. => I<{j}>: Iterator>): Tuple::Bounds` - the most //! general way to bound components, allowing typle index expressions on both //! sides of the colon. Note that the suffix `: Tuple::Bounds` is required after @@ -115,11 +133,11 @@ //! fn multiply( //! s: S, // s: (S<0>,...) //! t: T, // t: (T<0>,...) -//! ) -> (typle!(i in .. => as Mul>>::Output)) // ( as Mul>>::Output,...) +//! ) -> (typle! {i in .. => as Mul>>::Output}) // ( as Mul>>::Output,...) //! where -//! typle!(i in .. => S<{i}>: Mul>): Tuple::Bounds, // S<0>: Mul>,... +//! typle! {i in .. => S<{i}>: Mul>}: Tuple::Bounds, // S<0>: Mul>,... //! { -//! (typle!(i in .. => s[[i]] * t[[i]])) // (s.0 * t.0,...) +//! (typle! {i in .. => s[[i]] * t[[i]]}) // (s.0 * t.0,...) //! } //! //! assert_eq!( @@ -138,7 +156,7 @@ //! [`cfg_attr`](https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute) //! except that the first argument is a boolean typle index expression. //! -//! ``` +//! ```rust //! # use typle::typle; //! #[cfg_attr(not(feature = "big-tuple"), typle(Tuple for 0..=12))] //! #[cfg_attr(feature = "big-tuple", typle(Tuple for 0..=24))] @@ -164,7 +182,7 @@ //! successfully for `T::LEN == 0` because `T::LAST` only appears in an `else` branch that is //! not compiled when `T::LEN == 0`. //! -//! ``` +//! ```rust //! # use typle::typle; //! # #[derive(Clone)] //! # struct Input {} @@ -184,7 +202,7 @@ //! T: Tuple, //! T<_>: HandleStuff, //! { -//! type Output = (typle!(i in .. => T<{i}>::Output)); +//! type Output = (typle! {i in .. => T<{i}>::Output}); //! //! // Return a tuple of output from each handler applied to the same input. //! fn handle_stuff(&self, input: Input) -> Self::Output { @@ -192,7 +210,9 @@ //! () //! } else { //! ( -//! typle!(i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone())), +//! typle! { +//! i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) +//! }, //! // Avoid expensive clone for the last handler. //! self.handlers[[T::LAST]].handle_stuff(input), //! ) @@ -244,7 +264,7 @@ //! # use typle::typle; //! #[typle(Tuple for 0..=12)] //! fn sum>(t: T) -> u32 { -//! typle_fold!(0; i in .. => |total| total + t[[i]]) +//! typle_fold! {0; i in .. => |total| total + t[[i]]} //! } //! //! assert_eq!(sum(()), 0); @@ -331,8 +351,13 @@ //! T<_>: Extract, //! { //! // The output of all previous components plus the state of the current component. -//! S = typle_variant!(i in ..T::MAX => -//! (typle!(j in ..i => T::<{j}>::Output), Option::State>) +//! S = typle_variant!( +//! curr in ..T::MAX => ( +//! typle! { +//! prev in ..curr => T::<{prev}>::Output +//! } +//! ), +//! Option::State> //! ), //! } //! ``` @@ -414,7 +439,7 @@ //! // the state of the current component. //! type State = TupleSequenceState>; //! // The final output is a tuple of outputs from all components. -//! type Output = (typle!(i in .. => as Extract>::Output)); +//! type Output = (typle! {i in .. => as Extract>::Output}); //! //! fn extract(&self, state: Option) -> Self::Output { //! // When LEN == 1 the code never changes `state` @@ -537,35 +562,6 @@ //! T: Tuple, //! {} //! ``` -//! - A `typle!` macro that contains a bound can only provide one bound. It is not -//! possible to use `+` to provide multiple bounds. -//! ```rust -//! # use typle::typle; -//! # use std::{ops::{Add, Mul}, time::Duration}; -//! #[typle(Tuple for 0..=3)] -//! fn sum_and_product( -//! s: S, -//! t: T, -//! ) -> (typle![i in ..T::LEN * 2 => if i % 2 == 0 { -//! as Add>>::Output -//! } else { -//! as Mul>>::Output -//! }]) -//! where -//! S<_>: Copy, -//! T<_>: Copy, -//! // Cannot use `typle!(i in .. => S<{i}>: Add> + Mul>)` -//! // but can add bounds in separate macros. -//! typle!(i in .. => S<{i}>: Add>): Tuple::Bounds, -//! typle!(i in .. => S<{i}>: Mul>): Tuple::Bounds, -//! { -//! (typle![i in ..T::LEN * 2 => if i % 2 == 0 { -//! s[[i / 2]] + t[[i / 2]] -//! } else { -//! s[[i / 2]] * t[[i / 2]] -//! }]) -//! } -//! ``` //! - Standalone `async` and `unsafe` functions are not supported. //! - Standalone functions require explicit lifetimes on references: //! ```rust diff --git a/tests/compile/doc_typle.rs b/tests/compile/doc_typle.rs index 6735853..4e1e509 100644 --- a/tests/compile/doc_typle.rs +++ b/tests/compile/doc_typle.rs @@ -62,8 +62,13 @@ mod tuple { T: Tuple, T<_>: Extract, { - S = typle_variant!(i in ..T::MAX => - (typle!(j in ..i => T::<{j}>::Output)), Option::State> + S = typle_variant!( + curr in ..T::MAX => ( + typle! { + prev in ..curr => T::<{prev}>::Output + } + ), + Option::State> ), } @@ -79,7 +84,7 @@ mod tuple { // The state contains the output from all previous components and the state // of the current component. type State = TupleSequenceState>; - type Output = (typle!(i in .. => as Extract>::Output)); + type Output = (typle! {i in .. => as Extract>::Output}); fn extract(&self, state: Option) -> Self::Output { #[typle_attr_if(T::LEN == 1, allow(unused_mut))] diff --git a/tests/compile/mod.expanded.rs b/tests/compile/mod.expanded.rs index cd80bf0..048a9f7 100644 --- a/tests/compile/mod.expanded.rs +++ b/tests/compile/mod.expanded.rs @@ -2380,6 +2380,139 @@ pub mod pattern { <(T, u32) as _typle_fn_multiply_by>::apply((t, m)) } } +pub mod spread { + use std::ops::{Add, Mul}; + use typle::typle; + #[allow(non_camel_case_types)] + trait _typle_fn_duplicate_components { + type Return; + fn apply(self) -> Self::Return; + } + impl _typle_fn_duplicate_components for ((),) { + type Return = (); + fn apply(self) -> Self::Return { + #[allow(unused_variables)] + let (t,) = self; + { () } + } + } + impl _typle_fn_duplicate_components for ((T0,),) + where + T0: Clone, + { + type Return = (T0, T0); + fn apply(self) -> Self::Return { + let (t,) = self; + { (t.0.clone(), t.0) } + } + } + impl _typle_fn_duplicate_components for ((T0, T1),) + where + T0: Clone, + T1: Clone, + { + type Return = (T0, T0, T1, T1); + fn apply(self) -> Self::Return { + let (t,) = self; + { (t.0.clone(), t.0, t.1.clone(), t.1) } + } + } + impl _typle_fn_duplicate_components for ((T0, T1, T2),) + where + T0: Clone, + T1: Clone, + T2: Clone, + { + type Return = (T0, T0, T1, T1, T2, T2); + fn apply(self) -> Self::Return { + let (t,) = self; + { (t.0.clone(), t.0, t.1.clone(), t.1, t.2.clone(), t.2) } + } + } + fn duplicate_components(t: T) -> <(T,) as _typle_fn_duplicate_components>::Return + where + (T,): _typle_fn_duplicate_components, + { + <(T,) as _typle_fn_duplicate_components>::apply((t,)) + } + #[allow(non_camel_case_types)] + trait _typle_fn_sum_and_product { + type Return; + fn apply(self) -> Self::Return; + } + impl _typle_fn_sum_and_product for ((), ()) { + type Return = (); + fn apply(self) -> Self::Return { + #[allow(unused_variables)] + let (s, t) = self; + { () } + } + } + impl _typle_fn_sum_and_product for ((S0,), (T0,)) + where + S0: Copy, + T0: Copy, + S0: Add + Mul, + { + type Return = (>::Output, >::Output); + fn apply(self) -> Self::Return { + let (s, t) = self; + { (s.0 + t.0, s.0 * t.0) } + } + } + impl _typle_fn_sum_and_product for ((S0, S1), (T0, T1)) + where + S0: Copy, + S1: Copy, + T0: Copy, + T1: Copy, + S0: Add + Mul, + S1: Add + Mul, + { + type Return = ( + >::Output, + >::Output, + >::Output, + >::Output, + ); + fn apply(self) -> Self::Return { + let (s, t) = self; + { (s.0 + t.0, s.0 * t.0, s.1 + t.1, s.1 * t.1) } + } + } + impl _typle_fn_sum_and_product + for ((S0, S1, S2), (T0, T1, T2)) + where + S0: Copy, + S1: Copy, + S2: Copy, + T0: Copy, + T1: Copy, + T2: Copy, + S0: Add + Mul, + S1: Add + Mul, + S2: Add + Mul, + { + type Return = ( + >::Output, + >::Output, + >::Output, + >::Output, + >::Output, + >::Output, + ); + fn apply(self) -> Self::Return { + let (s, t) = self; + { (s.0 + t.0, s.0 * t.0, s.1 + t.1, s.1 * t.1, s.2 + t.2, s.2 * t.2) } + } + } + fn sum_and_product(s: S, t: T) -> <(S, T) as _typle_fn_sum_and_product>::Return + where + (S, T): _typle_fn_sum_and_product, + { + <(S, T) as _typle_fn_sum_and_product>::apply((s, t)) + } +} pub mod type_alias { #![allow(type_alias_bounds, unused)] use typle::typle; diff --git a/tests/compile/mod.rs b/tests/compile/mod.rs index 5dbda91..2b85fea 100644 --- a/tests/compile/mod.rs +++ b/tests/compile/mod.rs @@ -7,6 +7,7 @@ pub mod issue1; pub mod macros; pub mod method; pub mod pattern; +pub mod spread; pub mod type_alias; pub mod typle_args; pub mod typle_fold; diff --git a/tests/compile/spread.rs b/tests/compile/spread.rs new file mode 100644 index 0000000..4b6dc49 --- /dev/null +++ b/tests/compile/spread.rs @@ -0,0 +1,30 @@ +use std::ops::{Add, Mul}; + +use typle::typle; + +#[typle(Tuple for 0..=3)] +fn duplicate_components( + t: T, +) -> (typle! { + i in .. => T<{i}>, T<{i}> + }) +where + T<_>: Clone, +{ + (typle! { + i in .. => t[[i]].clone(), t[[i]] + }) +} + +#[typle(Tuple for 0..=3)] +fn sum_and_product( + s: S, + t: T, +) -> (typle! {i in .. => as Add>>::Output, as Mul>>::Output}) +where + S<_>: Copy, + T<_>: Copy, + typle!(i in .. => S<{i}>: Add> + Mul>): Tuple::Bounds, +{ + (typle! {i in .. => s[[i]] + t[[i]], s[[i]] * t[[i]]}) +} diff --git a/tests/compile/typle_args.rs b/tests/compile/typle_args.rs index f54c488..78af77e 100644 --- a/tests/compile/typle_args.rs +++ b/tests/compile/typle_args.rs @@ -94,7 +94,7 @@ where T: Tuple, // `T`` is a tuple with 0 to 3 components. T<_>: HandleStuff, // All components implement `HandleStuff`. { - type Output = (typle!(i in .. => T<{i}>::Output)); + type Output = (typle! {i in .. => T<{i}>::Output}); // Return a tuple of output from each handler applied to the same input. fn handle_stuff(&self, input: Input) -> Self::Output { @@ -102,7 +102,9 @@ where () } else { ( - typle!(i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone())), + typle! { + i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) + }, // Avoid expensive clone for the last handler. self.handlers[[T::LAST]].handle_stuff(input), )