nom icon indicating copy to clipboard operation
nom copied to clipboard

Type coercion for `Input` in `8.0.0`

Open IpFruion opened this issue 11 months ago • 6 comments

Problem

When upgrading from 7.* to 8.0.0 I noticed that tag(b"hello") now produces an error.

the trait `Input` is not implemented for `&[u8; 5]`

This is due to the shift from the InputLength trait being implemented for tag in 7.* https://docs.rs/nom/7.1.3/nom/bytes/complete/fn.tag.html

To tag must implement Input in 8.0.0: https://docs.rs/nom/8.0.0/nom/bytes/complete/fn.tag.html

Interim Solution

A way to fix this in the interim is to coerce the type into &[u8] which Input is implemented for. i.e. tag(b"hello" as &[u8]).

Possible Fix

Implement Input for &[u8; N] for any const N: usize

IpFruion avatar Jan 26 '25 21:01 IpFruion

I met the same problem today. This isn't possible to implement to impl<'a, const N: usize> Input for &'a [u8; N] because of Input::take (and other sibling methods) which returns Self (or (Self, Self) in the case of take_split):

https://github.com/rust-bakery/nom/blob/2cec1b3e4c9ccac62c902d60c00de6d1549ccbe1/src/traits.rs#L42-L47

We may want to refactor that by splitting the Input trait into 2 traits. Thoughts @Geal? I didn't check the implication of that yet. It's probably more trait bound checks for a couple of combinators.

Hywan avatar Jan 27 '25 13:01 Hywan

Alternatively, we can add another associated type for the output of take() & siblings, à laIter & IterIndices:

https://github.com/rust-bakery/nom/blob/2cec1b3e4c9ccac62c902d60c00de6d1549ccbe1/src/traits.rs#L23-L36

Something like:

pub trait Input: Clone + Sized {
    // …

    type TakeOutput;

    fn take(&self, index: usize) -> Self::TakeOutput;

    // …
}

Hywan avatar Jan 27 '25 13:01 Hywan

that might make it way more complex. Are there other alternatives? maybe making Parser::parse take a Into<I> ?

Geal avatar Jan 29 '25 08:01 Geal

@Hywan Yeah that makes sense why that won't do it.

@Geal Another option to the list other than splitting up the Input trait could be a separate one that can do the conversion i.e.

pub trait IntoInput {
    type Input: Input;
    fn into_input(self) -> Self::Input;
}

impl<'a, const N: usize> IntoInput for &'a [u8; N] {
    type Input = &'a [u8];
    fn into_input(self) -> Self::Input {
        self as &[u8]
    }
}

impl<T: Input> IntoInput for T {
    type Input = T;
    fn into_input(self) -> Self::Input {
        self
    }
}

fn tag<I>(input: I) where I: IntoInput {
   let input = input.into_input();
}

Or something of the like. Similar to the Into<I> but with the trait bounds.

IpFruion avatar Jan 29 '25 16:01 IpFruion

I like this IntoInput solution. This could applied in more places, where we transparently translate any parameter that supports it into the correctly supported Input type. You can also provide a good standard implementation such that no API breaks, but you do accept way more types as inputs.

marcdejonge avatar Jan 30 '25 09:01 marcdejonge

Something like this?

https://github.com/rust-bakery/nom/pull/1810/files#diff-172747fc43e38d33de743b2d5f564ad6440570670af7aa912235399448a55c7cR570

marcdejonge avatar Jan 30 '25 14:01 marcdejonge