提问人:Richard Neumann 提问时间:11/10/2023 最后编辑:Richard Neumann 更新时间:11/10/2023 访问量:61
在宏中捕获单态泛型
Capture monomorphized generics in a macro
问:
我编写了一个特征来将对象序列化为小字节序字节的迭代器:
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
}
}
但是,由于此代码旨在运行在硬件性能低下的嵌入式系统上,因此我想避免堆分配,因此希望摆脱 es。Box
当然,在基本的 rust 中链接迭代器是不可能的,因为每次调用都会返回一种新型迭代器。
因此,我认为也许宏可以做到这一点,因为我已经为该特征的派生宏做了类似的事情。.chain()
但是,我当然不想为任何可能的 和 实现正文,而只为那些在相应程序中使用的正文实现。
因此,我需要在单态代码上运行宏。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)
}
}
答:
0赞
Richard Neumann
11/10/2023
#1
多亏了 Chayim 的提示,我才让它工作起来:
#![cfg(feature = "heapless")]
use crate::ToLeBytes;
use std::array::IntoIter;
#[derive(Debug)]
pub enum SizePrefixIterator {
U8(IntoIter<u8, 1>),
U16(IntoIter<u8, 2>),
U32(IntoIter<u8, 4>),
U64(IntoIter<u8, 8>),
}
impl SizePrefixIterator {
#[allow(clippy::cast_possible_truncation)]
pub fn new(len: usize, capacity: usize) -> Self {
if u8::try_from(capacity).is_ok() {
Self::U8(<u8 as ToLeBytes>::to_le_bytes(len as u8))
} else if u16::try_from(capacity).is_ok() {
Self::U16(<u16 as ToLeBytes>::to_le_bytes(len as u16))
} else if u32::try_from(capacity).is_ok() {
Self::U32(<u32 as ToLeBytes>::to_le_bytes(len as u32))
} else if u64::try_from(capacity).is_ok() {
Self::U64(<u64 as ToLeBytes>::to_le_bytes(len as u64))
} else {
unreachable!("container size exceeds u64");
}
}
}
impl Iterator for SizePrefixIterator {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::U8(header) => header.next(),
Self::U16(header) => header.next(),
Self::U32(header) => header.next(),
Self::U64(header) => header.next(),
}
}
}
#[cfg(feature = "heapless")]
impl<T, const SIZE: usize> ToLeBytes for heapless::Vec<T, SIZE>
where
T: Sized + ToLeBytes,
{
type Iter = std::iter::Chain<
size_prefix_iterator::SizePrefixIterator,
FlatMap<
<Self as IntoIterator>::IntoIter,
<T as ToLeBytes>::Iter,
fn(T) -> <T as ToLeBytes>::Iter,
>,
>;
fn to_le_bytes(self) -> Self::Iter {
size_prefix_iterator::SizePrefixIterator::new(self.len(), SIZE).chain(
self.into_iter()
.flat_map(<T as ToLeBytes>::to_le_bytes as fn(T) -> <T as ToLeBytes>::Iter),
)
}
}
上一个:SAS 宏取消引用
评论
chain()
flat_map()
flat_map()
适用于 .但是,我需要预置大小,其类型仅在单调期间确定(参见 if/else)。但我会尝试自定义迭代器方法。heapless::Vec
chain()
&self
self
&'a heapless::Vec
flat_map()
chain()