Skip to content

A winnow::token::take_n::<const N: usize> could create &[T; N] #823

Description

@onkoe

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-combinatorArea: combinatorsC-enhancementCategory: Raise on the bar on expectations

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions