在宏中捕获单态泛型

问题描述 投票:0回答:1

我编写了一个特征,将对象序列化为小端字节的迭代器:

pub trait ToLeBytes: Sized
where
    Self::Iter: Iterator<Item = u8>,
{
    type Iter;

    fn to_le_bytes(&self) -> Self::Iter;
}

我已经为我需要的原始数据类型以及

heapless::Vec
实现了它:

#[allow(clippy::cast_possible_truncation)]
#[cfg(feature = "heapless")]
impl<I, const SIZE: usize> ToLeBytes for heapless::Vec<I, SIZE>
where
    I: ToLeBytes,
    for<'a> <I as ToLeBytes>::Iter: Iterator<Item = u8> + 'a,
{
    type Iter = Box<dyn Iterator<Item = u8>>;

    fn to_le_bytes(&self) -> Self::Iter {
        let mut iterator: Box<dyn Iterator<Item = u8>> = Box::new(empty());

        if u8::try_from(SIZE).is_ok() {
            iterator = Box::new(<u8 as ToLeBytes>::to_le_bytes(&(self.len() as u8)));
        } else if u16::try_from(SIZE).is_ok() {
            iterator = Box::new(<u16 as ToLeBytes>::to_le_bytes(&(self.len() as u16)));
        } else if u32::try_from(SIZE).is_ok() {
            iterator = Box::new(<u32 as ToLeBytes>::to_le_bytes(&(self.len() as u32)));
        } else if u64::try_from(SIZE).is_ok() {
            iterator = Box::new(<u64 as ToLeBytes>::to_le_bytes(&(self.len() as u64)));
        }

        for item in self {
            iterator = Box::new(iterator.chain(<I as ToLeBytes>::to_le_bytes(item)));
        }

        iterator
    }
}

但是,由于此代码旨在在具有低性能硬件的嵌入式系统上运行,因此我想避免堆分配,从而希望摆脱

Box
es。

当然,在基本 Rust 中链接迭代器是不可能的,因为每次调用

.chain()
都会返回一个新类型的迭代器。 因此,我认为也许宏可以做到这一点,因为我已经为该特征的derive macro做了类似的事情。

但是,我当然不想为任何可能的

I
SIZE
实现主体,而只想为相应程序中使用的主体实现。 因此,我需要在单态代码上运行宏。

我尝试用谷歌搜索如何做到这一点,但没有找到任何结果。 如何编写在

impl
块的单态代码中传递的宏? 我不需要完整的解决方案,而是推动正确的方向。

更新

我想我已经快到了,感谢 Chayim 的评论:

use crate::ToLeBytes;
use std::array::IntoIter;
use std::iter::FlatMap;
use std::slice::Iter;

pub struct ContainerIterator<'a, T, const HEADER_SIZE: usize>
where
    T: ToLeBytes,
{
    size_iterator: IntoIter<u8, HEADER_SIZE>,
    items_iterator: FlatMap<Iter<'a, T>, <T as ToLeBytes>::Iter, fn(&T) -> <T as ToLeBytes>::Iter>,
}

impl<'a, T, const HEADER_SIZE: usize> ContainerIterator<'a, T, HEADER_SIZE>
where
    T: ToLeBytes,
{
    fn from_size_iterator_and_slice(size_iterator: IntoIter<u8, HEADER_SIZE>, items: &[T]) -> Self
    where
        T: ToLeBytes,
    {
        Self {
            size_iterator,
            items_iterator: items
                .iter()
                .flat_map(|item| <T as ToLeBytes>::to_le_bytes(item)),
        }
    }
}

impl<'a, T, const HEADER_SIZE: usize> Iterator for ContainerIterator<'a, T, HEADER_SIZE>
where
    T: ToLeBytes,
{
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next_header) = self.size_iterator.next() {
            Some(next_header)
        } else {
            self.items_iterator.next()
        }
    }
}

pub enum SizedContainerIterator<'a, T>
where
    T: ToLeBytes,
{
    U8(ContainerIterator<'a, T, 1>),
    U16(ContainerIterator<'a, T, 2>),
    U32(ContainerIterator<'a, T, 4>),
    U64(ContainerIterator<'a, T, 8>),
}

impl<'a, T> SizedContainerIterator<'a, T>
where
    T: ToLeBytes,
{
    pub fn new(items: &[T], capacity: usize) -> SizedContainerIterator<'a, T>
    where
        T: ToLeBytes,
    {
        if u8::try_from(capacity).is_ok() {
            SizedContainerIterator::U8(ContainerIterator::from_size_iterator_and_slice(
                <u8 as ToLeBytes>::to_le_bytes(&(items.len() as u8)),
                items,
            ))
        } else if u16::try_from(capacity).is_ok() {
            SizedContainerIterator::U16(ContainerIterator::from_size_iterator_and_slice(
                <u16 as ToLeBytes>::to_le_bytes(&(items.len() as u16)),
                items,
            ))
        } else if u32::try_from(capacity).is_ok() {
            SizedContainerIterator::U32(ContainerIterator::from_size_iterator_and_slice(
                <u32 as ToLeBytes>::to_le_bytes(&(items.len() as u32)),
                items,
            ))
        } else if u64::try_from(capacity).is_ok() {
            SizedContainerIterator::U64(ContainerIterator::from_size_iterator_and_slice(
                <u64 as ToLeBytes>::to_le_bytes(&(items.len() as u64)),
                items,
            ))
        } else {
            unreachable!("vec size exceeds u64");
        }
    }
}

impl<'a, T> Iterator for SizedContainerIterator<'a, T>
where
    T: ToLeBytes,
{
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Self::U8(iterator) => iterator.next(),
            Self::U16(iterator) => iterator.next(),
            Self::U32(iterator) => iterator.next(),
            Self::U64(iterator) => iterator.next(),
        }
    }
}

但是,现在我遇到了指定

Iter
类型的生命周期的问题:

#[allow(clippy::cast_possible_truncation)]
#[cfg(feature = "heapless")]
impl<I, const SIZE: usize> ToLeBytes for heapless::Vec<I, SIZE>
where
    I: ToLeBytes,
    for<'a> <I as ToLeBytes>::Iter: Iterator<Item = u8> + 'a,
{
    type Iter = SizedContainerIterator<'_, I>;

    fn to_le_bytes(&self) -> Self::Iter {
        SizedContainerIterator::new(self, SIZE)
    }
}
rust macros monomorphism
1个回答
0
投票

感谢 Chayim 的提示,我成功了:

use crate::ToLeBytes;
use std::array::IntoIter;
use std::iter::FlatMap;

type ItemFlatMap<T> = FlatMap<
    <T as IntoIterator>::IntoIter,
    <<T as IntoIterator>::Item as ToLeBytes>::Iter,
    fn(<T as IntoIterator>::Item) -> <<T as IntoIterator>::Item as ToLeBytes>::Iter,
>;

pub enum ContainerIterator<T>
where
    T: IntoIterator,
    <T as IntoIterator>::Item: ToLeBytes,
{
    Const(ItemFlatMap<T>),
    U8(IntoIter<u8, 1>, ItemFlatMap<T>),
    U16(IntoIter<u8, 2>, ItemFlatMap<T>),
    U32(IntoIter<u8, 4>, ItemFlatMap<T>),
    U64(IntoIter<u8, 8>, ItemFlatMap<T>),
}

impl<T> ContainerIterator<T>
where
    T: IntoIterator,
    <T as IntoIterator>::Item: ToLeBytes,
{
    #[allow(clippy::cast_possible_truncation)]
    fn dynamically_sized(items: T, len: usize, capacity: usize) -> Self {
        if u8::try_from(capacity).is_ok() {
            Self::U8(
                <u8 as ToLeBytes>::to_le_bytes(len as u8),
                items
                    .into_iter()
                    .flat_map(<<T as IntoIterator>::Item as ToLeBytes>::to_le_bytes),
            )
        } else if u16::try_from(capacity).is_ok() {
            Self::U16(
                <u16 as ToLeBytes>::to_le_bytes(len as u16),
                items
                    .into_iter()
                    .flat_map(<<T as IntoIterator>::Item as ToLeBytes>::to_le_bytes),
            )
        } else if u32::try_from(capacity).is_ok() {
            Self::U32(
                <u32 as ToLeBytes>::to_le_bytes(len as u32),
                items
                    .into_iter()
                    .flat_map(<<T as IntoIterator>::Item as ToLeBytes>::to_le_bytes),
            )
        } else if u64::try_from(capacity).is_ok() {
            Self::U64(
                <u64 as ToLeBytes>::to_le_bytes(len as u64),
                items
                    .into_iter()
                    .flat_map(<<T as IntoIterator>::Item as ToLeBytes>::to_le_bytes),
            )
        } else {
            unreachable!("vec size exceeds u64");
        }
    }

    fn const_sized(items: T) -> Self {
        Self::Const(
            items
                .into_iter()
                .flat_map(<<T as IntoIterator>::Item as ToLeBytes>::to_le_bytes),
        )
    }
}

impl<T> Iterator for ContainerIterator<T>
where
    T: IntoIterator,
    <T as IntoIterator>::Item: ToLeBytes,
{
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Self::Const(items) => items.next(),
            Self::U8(header, items) => header.next().map_or_else(|| items.next(), Some),
            Self::U16(header, items) => header.next().map_or_else(|| items.next(), Some),
            Self::U32(header, items) => header.next().map_or_else(|| items.next(), Some),
            Self::U64(header, items) => header.next().map_or_else(|| items.next(), Some),
        }
    }
}

impl<T, const SIZE: usize> From<[T; SIZE]> for ContainerIterator<[T; SIZE]>
where
    T: ToLeBytes,
{
    fn from(array: [T; SIZE]) -> Self {
        Self::const_sized(array)
    }
}

impl<T> From<Vec<T>> for ContainerIterator<Vec<T>>
where
    T: ToLeBytes,
{
    fn from(vec: Vec<T>) -> Self {
        let len = vec.len();
        let capacity = vec.capacity();
        Self::dynamically_sized(vec, len, capacity)
    }
}

#[cfg(feature = "heapless")]
impl<T, const SIZE: usize> From<heapless::Vec<T, SIZE>>
    for ContainerIterator<heapless::Vec<T, SIZE>>
where
    T: ToLeBytes,
{
    fn from(vec: heapless::Vec<T, SIZE>) -> Self {
        let len = vec.len();
        Self::dynamically_sized(vec, len, SIZE)
    }
}

impl<T, const SIZE: usize> ToLeBytes for [T; SIZE]
where
    T: ToLeBytes,
{
    type Iter = ContainerIterator<Self>;

    fn to_le_bytes(self) -> Self::Iter {
        ContainerIterator::from(self)
    }
}

#[allow(clippy::cast_possible_truncation)]
#[cfg(feature = "heapless")]
impl<T, const SIZE: usize> ToLeBytes for heapless::Vec<T, SIZE>
where
    T: ToLeBytes,
{
    type Iter = ContainerIterator<Self>;

    fn to_le_bytes(self) -> Self::Iter {
        ContainerIterator::from(self)
    }
}

我决定让特征方法现在取得所有权(即消耗)

self
以避免生命周期带来的无尽麻烦。 另外,由于某种原因,编译器不接受类型
std::iter::Chain<IntoIter<u8, 1>, ItemFlatMap<T>>
for

<u8 as ToLeBytes>::to_le_bytes(len as u8).chain(
items
    .into_iter()
    .flat_map(<<T as IntoIterator>::Item as ToLeBytes>::to_le_bytes)
)

这就是为什么我通过将迭代器的两个部分分别存储在变体中来解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.