首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用迭代器项抽象过可变性

用迭代器项抽象过可变性
EN

Stack Overflow用户
提问于 2022-06-05 16:09:58
回答 1查看 102关注 0票数 1

我编写了一个简单的助手来循环在u8片中的小块(4位)。它在& u8上使用内部迭代器,本质上使步骤加倍,其中两个步骤都引用相同的底层u8,但在查看时过滤和移动比特。

我还使用RcRefCell创建了一个可变版本(而不是粘贴在这里),这需要在&mut u8上使用底层迭代器。不过,我希望只读版本也能与提供可变访问权限的迭代器一起工作。

我尝试过使用I: 'a + Borrow<u8>, T: Iterator<Item = I>代替硬编码的&'a u8AsRef<u8>,但是失败了,因为随着内部字节变成非引用,借用发生在我的next()方法中,借出的值将避开它们的闭包。

允许我的Nibbler使用在&u8&mut u8上迭代的迭代器需要什么?

代码语言:javascript
复制
pub enum Nibble<'a> {
    MSB(&'a u8),
    LSB(&'a u8),
}

impl Nibble<'_> {
    pub fn from_u8(input: &u8) -> (Nibble, Nibble) {
        let msb = Nibble::MSB(input);
        let lsb = Nibble::LSB(input);
        (msb, lsb)
    }

    pub fn get(&self) -> u8 {
        match self {
            Nibble::MSB(r) => (**r & 0b11110000) >> 4,
            Nibble::LSB(r) => **r & 0b00001111,
        }
    }
}

pub struct Nibbler<'a, T> {
    rest: Option<Nibble<'a>>,
    inner: T,
}

impl<T> Nibbler<'_, T> {
    pub fn new(inner: T) -> Self {
        Nibbler { inner, rest: None }
    }
}

impl<'a, T: Iterator<Item = &'a u8>> Iterator for Nibbler<'a, T> {
    type Item = Nibble<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        self.rest.take().or_else(|| {
            self.inner.next().map(|byte| {
                let (msb, lsb) = Nibble::from_u8(byte);
                self.rest = Some(msb);
                lsb
            })
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_nibble_get() {
        let val = 0x79;
        let (msb, lsb) = Nibble::from_u8(&val);
        assert_eq!(msb.get(), 7);
        assert_eq!(lsb.get(), 9);
    }

    #[test]
    fn test_nibbler() {
        let t = [0x12, 0x34, 0x56, 0x78];
        for (i, nibble) in Nibbler::new(t.iter()).enumerate() {
            match i {
                0 => assert_eq!(nibble.get(), 2),
                1 => assert_eq!(nibble.get(), 1),
                2 => assert_eq!(nibble.get(), 4),
                3 => assert_eq!(nibble.get(), 3),
                4 => assert_eq!(nibble.get(), 6),
                5 => assert_eq!(nibble.get(), 5),
                6 => assert_eq!(nibble.get(), 8),
                7 => assert_eq!(nibble.get(), 7),
                _ => {}
            }
        }
    }

    // #[test]
    // fn test_nibbler_mut() {
    //     let t = [0x12, 0x34, 0x56, 0x78];
    //     for (i, nibble) in Nibbler::new(t.iter_mut()).enumerate() {
    //         match i {
    //             0 => assert_eq!(nibble.get(), 2),
    //             1 => assert_eq!(nibble.get(), 1),
    //             2 => assert_eq!(nibble.get(), 4),
    //             3 => assert_eq!(nibble.get(), 3),
    //             4 => assert_eq!(nibble.get(), 6),
    //             5 => assert_eq!(nibble.get(), 5),
    //             6 => assert_eq!(nibble.get(), 8),
    //             7 => assert_eq!(nibble.get(), 7),
    //             _ => {}
    //         }
    //     }
    // }
}

正如@chayim所要求的,下面是我对Borrow的尝试

代码语言:javascript
复制
use std::borrow::Borrow;

impl<'a, I: Borrow<u8> + 'a, T: Iterator<Item = I>> Iterator for Nibbler<'a, T> {
    type Item = Nibble<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        self.rest.take().or_else(|| {
            self.inner.next().map(|byte| {
                let (msb, lsb) = Nibble::from_u8(byte.borrow());
                self.rest = Some(msb);
                lsb
            })
        })
    }
}

哪种错误

代码语言:javascript
复制
error[E0515]: cannot return value referencing function parameter `byte`
  --> src/utils/nibbler2.rs:42:17
   |
40 |                 let (msb, lsb) = Nibble::from_u8(byte.borrow());
   |                                                  ------------- `byte` is borrowed here
41 |                 self.rest = Some(msb);
42 |                 lsb
   |                 ^^^ returns a value referencing data owned by the current function
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-06-07 12:39:37

经过一段时间的努力,我终于在this answer中找到了解决方案。

代码语言:javascript
复制
pub enum Nibble<'a> {
    MSB(&'a u8),
    LSB(&'a u8),
}

impl Nibble<'_> {
    pub fn from_u8(input: &u8) -> (Nibble, Nibble) {
        let msb = Nibble::MSB(input);
        let lsb = Nibble::LSB(input);
        (msb, lsb)
    }

    pub fn get(&self) -> u8 {
        match self {
            Nibble::MSB(r) => (**r & 0b11110000) >> 4,
            Nibble::LSB(r) => **r & 0b00001111,
        }
    }
}

pub struct Nibbler<'a, T> {
    rest: Option<Nibble<'a>>,
    inner: T,
}

impl<T> Nibbler<'_, T> {
    pub fn new(inner: T) -> Self {
        Nibbler { inner, rest: None }
    }
}

impl<'a, T> Iterator for Nibbler<'a, T>
where
    T: Iterator,
    <T as Iterator>::Item: IntoNibbleRef<'a>,
{
    type Item = Nibble<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        self.rest.take().or_else(|| {
            self.inner.next().map(|byte| {
                let (msb, lsb) = Nibble::from_u8(byte.into_nibble_ref());
                self.rest = Some(msb);
                lsb
            })
        })
    }
}

trait IntoNibbleRef<'a> {
    fn into_nibble_ref(self) -> &'a u8;
}

impl<'a> IntoNibbleRef<'a> for &'a u8 {
    fn into_nibble_ref(self) -> &'a u8 {
        self
    }
}

impl<'a> IntoNibbleRef<'a> for &'a mut u8 {
    fn into_nibble_ref(self) -> &'a u8 {
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_nibble_get() {
        let val = 0x79;
        let (msb, lsb) = Nibble::from_u8(&val);
        assert_eq!(msb.get(), 7);
        assert_eq!(lsb.get(), 9);
    }

    #[test]
    fn test_nibbler() {
        let t = [0x12, 0x34, 0x56, 0x78];
        for (i, nibble) in Nibbler::new(t.iter()).enumerate() {
            match i {
                0 => assert_eq!(nibble.get(), 2),
                1 => assert_eq!(nibble.get(), 1),
                2 => assert_eq!(nibble.get(), 4),
                3 => assert_eq!(nibble.get(), 3),
                4 => assert_eq!(nibble.get(), 6),
                5 => assert_eq!(nibble.get(), 5),
                6 => assert_eq!(nibble.get(), 8),
                7 => assert_eq!(nibble.get(), 7),
                _ => {}
            }
        }
    }

    #[test]
    fn test_nibbler_mut() {
        let mut t = [0x12, 0x34, 0x56, 0x78];
        for (i, nibble) in Nibbler::new(t.iter_mut()).enumerate() {
            match i {
                0 => assert_eq!(nibble.get(), 2),
                1 => assert_eq!(nibble.get(), 1),
                2 => assert_eq!(nibble.get(), 4),
                3 => assert_eq!(nibble.get(), 3),
                4 => assert_eq!(nibble.get(), 6),
                5 => assert_eq!(nibble.get(), 5),
                6 => assert_eq!(nibble.get(), 8),
                7 => assert_eq!(nibble.get(), 7),
                _ => {}
            }
        }
    }
}

您需要引入另一个可以将&u8&mut u8转换为&u8的嵌套特性,这里称为IntoNibbleRef

在进行了更多的实验之后,我意识到您也可以泛泛地实现这样一个特性:

代码语言:javascript
复制
impl<'a, T> Iterator for Nibbler<'a, T>
where
    T: Iterator,
    <T as Iterator>::Item: IntoImmutableRef<'a, u8>,
{
    type Item = Nibble<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        self.rest.take().or_else(|| {
            self.inner.next().map(|byte| {
                let (msb, lsb) = Nibble::from_u8(byte.into_immutable_ref());
                self.rest = Some(msb);
                lsb
            })
        })
    }
}
代码语言:javascript
复制
trait IntoImmutableRef<'a, T> {
    fn into_immutable_ref(self) -> &'a T;
}

impl<'a, T> IntoImmutableRef<'a, T> for &'a T {
    fn into_immutable_ref(self) -> &'a T {
        self
    }
}

impl<'a, T> IntoImmutableRef<'a, T> for &'a mut T {
    fn into_immutable_ref(self) -> &'a T {
        self
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72509006

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档