Please complete the following tasks
winnow version
0.7.11
Describe your use case
I'd like to avoid the following pattern:
let Ok(flags: &[u8; 3]) = take(3_usize)
.parse_next(input)?
.try_into()
else {
unreachable!("ck'd @ runtime that we got 3 elements in slice");
};
where take(3_usize) creates &[u8] instead of &[u8; 3].
This missing feature is the primary reason I avoided parser-combinator libraries like nom and winnow a couple years ago.
I've gotten better at designing parsers to avoid these checks, but they still show up at times, and, in my view, they're pretty noisy. Implementing binary protocols is muuuuuch more painful than it should be, as we know that winnow already checked the slice size at runtime.
Describe the solution you'd like
Using const generics would permit creating typed arrays/slices at compile time.
You could also make take a const fn for compile-time conversions by users, though I think that'd require const trait for const impl Iterator and const impl {TryFrom, TryInto}. I think it'd also have worse ergonomics, though it'd avoid adding another parser to the library.
In any case, I think winnow could have a const-generic take for slices right now. We could call it something like "take_n". Its usage might look something like this:
let flags: &[u8; 3] = take_n::<3_usize>.parse_next(input)?;
let flags_arr: [u8; 3] = *flags;
This solution only supports slices, so it wouldn't be appropriate to replace take in most cases. It also requires knowing N at compile time. So, winnow::token may not be the right place to put it...
(in the future, a const fn version might permit using other stream types by calling their methods as needed, though it'd still be a pretty limited subset)
Alternatives, if applicable
Users can apply unsafe/unchecked assertions, or they can just eat the checking costs at runtime (e.g., my_parser.try_map(TryInto::try_into).parse_next(input))
Either way, doing so is a little messy; sloppy refactors could leave downstream users with panic/UB-ridden code, especially in generic parsers or parsers with many layers of indirection.
Additional Context
Please complete the following tasks
winnow version
0.7.11
Describe your use case
I'd like to avoid the following pattern:
where
take(3_usize)creates&[u8]instead of&[u8; 3].This missing feature is the primary reason I avoided parser-combinator libraries like
nomandwinnowa couple years ago.I've gotten better at designing parsers to avoid these checks, but they still show up at times, and, in my view, they're pretty noisy. Implementing binary protocols is muuuuuch more painful than it should be, as we know that
winnowalready checked the slice size at runtime.Describe the solution you'd like
Using const generics would permit creating typed arrays/slices at compile time.
You could also make
takeaconst fnfor compile-time conversions by users, though I think that'd requireconst traitforconst impl Iteratorandconst impl {TryFrom, TryInto}. I think it'd also have worse ergonomics, though it'd avoid adding another parser to the library.In any case, I think
winnowcould have a const-generictakefor slices right now. We could call it something like "take_n". Its usage might look something like this:This solution only supports slices, so it wouldn't be appropriate to replace
takein most cases. It also requires knowingNat compile time. So,winnow::tokenmay not be the right place to put it...(in the future, a
const fnversion might permit using other stream types by calling their methods as needed, though it'd still be a pretty limited subset)Alternatives, if applicable
Users can apply
unsafe/unchecked assertions, or they can just eat the checking costs at runtime (e.g.,my_parser.try_map(TryInto::try_into).parse_next(input))Either way, doing so is a little messy; sloppy refactors could leave downstream users with panic/UB-ridden code, especially in generic parsers or parsers with many layers of indirection.
Additional Context