Let's say one wants to implement traits for "typed" regular expressions with capture groups:
trait RegexWithMatchGroups
Self: for <const N: usize in 0..Self::MATCH_GROUPS> HasMatchGroup<N>
{
fn re() -> &'static Regex;
const MATCH_GROUPS: usize;
}
trait HasMatchGroup<const N: usize>
where
// For N=0, the range is empty, so this trait is not implemented
Self: for <const M: usize in 0..N)> HasMatchGroup<M>,
{
// Sized by default
type Output;
}
That's the kind of syntax and semantics that I would like to see.
Interestingly enough, there's a pattern that can act as a variadic generic (see template parameter packs in C++). Because T::RegexWithMatchGroups
might have
T::HasMatchGroup::<0>::Output
,T::HasMatchGroup::<1>::Output
,T::HasMatchGroup::<2>::Output
,- ...
T::HasMatchGroup::<{T::RegexWithMatchGroups::MATCH_GROUPS - 1}>::Output
.
And there's a potential interaction with the adhoc
keyword that I thought up in https://internals.rust-lang.org/t/better-enums/16692.
// It defines a tuple of size `T::RegexWithMatchGroups::MATCH_GROUPS` with said types
let s: (adhoc for <const N: usize in 0..T::RegexWithMatchGroups::MATCH_GROUPS> T::HasMatchGroup::<N>::Output) = (adhoc for <const N: usize in 0..T::RegexWithMatchGroups::MATCH_GROUPS> {
let val: <T::HasMatchGroup::<N>::Output>;
// init and return val
})
Above, (adhoc for <const N: usize in 0..T::RegexWithMatchGroups::MATCH_GROUPS> T::HasMatchGroup::<N>::Output)
is a tuple with length T::RegexWithMatchGroups::MATCH_GROUPS
and types T::HasMatchGroup::<N>::Output
where N
iterates in the index range.
And
(adhoc for <const N: usize in 0..T::RegexWithMatchGroups::MATCH_GROUPS> {
let val: T::HasMatchGroup::<N>::Output;
// init and return val
})
is a map-like heterogeneous initializer.
Syntax
adhoc for i in 0..T::RegexWithMatchGroups::MATCH_GROUPS {
println!("{}", s.{i});
}
could be used for basic heterogeneous iteration rather than intialization. Here s.{i}
is a syntax for heterogeneous "indexing" into a tuple.