У меня есть библиотека синтаксического анализа Rust для буферов байтов с проводной кодировкой, и я пытаюсь создать структуру данных «представления» ByteSliceReader
для написания субпарсеров, которые обеспечивают проверку границ во время компиляции. Поскольку дженерики const, по-видимому, не обладают достаточной функциональностью для этого (поправьте меня, если я ошибаюсь), я попробовал использовать крейт typenum
. Однако я получаю странные/трудные для понимания ошибки компилятора, поэтому я ищу рекомендации по их устранению.
(Я не думаю, что смогу использовать generic-array
, поскольку субпарсерам необходимо использовать такие функции, как f32::from_le_bytes
, которые принимают [u8; 4]
.)
Структура данных выглядит примерно так (за исключением того, что на самом деле данные хранятся в кольцевом буфере, а не в массиве):
use typenum::*;
struct ByteSliceReader<'a, const LEN: usize, Cursor> {
buffer: &'a [u8; LEN],
cursor: core::marker::PhantomData<Cursor>,
}
Здесь Cursor
представляет собой беззнаковое целое число на основе typenum
, которое отслеживает, сколько байтов было использовано субпарсером. У него есть один метод для «выталкивания» количества байтов, определенного константным обобщением:
impl<'a, const LEN: usize, Cursor: Unsigned> ByteSliceReader<'a, LEN, Cursor>
where
Const<LEN>: ToUInt,
U<LEN>: Unsigned,
{
#[must_use]
fn pop<const NUM: usize>(self) -> (ByteSliceReader<'a, LEN, Sum<Cursor, U<NUM>>>, [u8; NUM])
where
Const<NUM>: ToUInt,
Cursor: core::ops::Add<U<NUM>>,
Sum<Cursor, U<NUM>>: IsLessOrEqual<U<LEN>>,
{
let mut byte_window = [0; NUM];
for (i, byte) in byte_window.iter_mut().enumerate() {
*byte = self.buffer[Cursor::to_usize() + i];
}
(
ByteSliceReader::<LEN, _> {
buffer: self.buffer,
cursor: core::marker::PhantomData,
},
byte_window,
)
}
}
Все это компилируется нормально. Но когда я пытаюсь использовать его в этом примере вспомогательной функции (предназначенной для извлечения и анализа некоторых байтов), она не компилируется:
fn get_serial_and_part_num<const LEN: usize, Cursor>(
reader: ByteSliceReader<'_, LEN, Cursor>,
) -> (ByteSliceReader<'_, LEN, Sum<Sum<Cursor, U1>, U1>>, (u8, u8))
where
Cursor: Unsigned,
Cursor: core::ops::Add<U1>,
Sum<Cursor, U1>: core::ops::Add<U1> + Unsigned,
Const<LEN>: ToUInt,
U<LEN>: Unsigned,
{
let (reader, serial_num) = reader.pop::<1>();
let (reader, part_num) = reader.pop::<1>();
(reader, (serial_num[0], part_num[0]))
}
error[E0599]: the method `pop` exists for struct `ByteSliceReader<'_, LEN, Cursor>`, but its trait bounds were not satisfied
--> fsw/sensors/ring_parser.rs:528:43
|
483 | struct ByteSliceReader<'a, const LEN: usize, Cursor> {
| ---------------------------------------------------- method `pop` not found for this struct
...
528 | let (reader, serial_num) = reader.pop::<1>();
| ^^^ method cannot be called on `ByteSliceReader<'_, LEN, Cursor>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Cursor: Add<<typenum::Const<_> as typenum::ToUInt>::Output>`
help: consider restricting the type parameter to satisfy the trait bound
|
526 | U<LEN>: Unsigned, Cursor: Add<<typenum::Const<_> as typenum::ToUInt>::Output>
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Предложение компилятора содержит _
, который я интерпретирую как 1, но его применение не помогает. Я пробовал различные перестановки границ и получал разные похожие ошибки. Есть ли способ добиться того, чего я хочу?
Непосредственная проблема заключается в том, что вам нужно сообщить компилятору, что Output
из Const<1> as ToUInt
будет U1
:
Const<1>: ToUInt<Output = U1>,
Однако это приводит к дальнейшим неудовлетворенным границам признаков (а именно к IsLessOrEqual
):
Sum<Cursor, U1>: IsLessOrEqual<U<LEN>>,
Sum<Sum<Cursor, U1>, U1>: IsLessOrEqual<U<LEN>>,