【问题标题】:Iterating over the contents of an Option, or over a specific value迭代选项的内容或特定值
【发布时间】:2014-11-17 13:54:23
【问题描述】:

假设我们有以下 C 代码(假设 srclen == dstlen 并且长度可以被 64 整除)。

void stream(uint8_t *dst, uint8_t *src, size_t dstlen) {
    int i;
    uint8_t block[64];
    while (dstlen > 64) {
        some_function_that_initializes_block(block);
        for (i=0; i<64; i++) {
            dst[i] = ((src != NULL)?src[i]:0) ^ block[i];
        }
        dst += 64;
        dstlen -= 64;
        if (src != NULL) { src += 64; }
    }
}

这是一个函数,它接受一个源和一个目标,并 xor 具有某个值的源 函数计算。当 source 设置为 NULL 指针时,dst 只是计算值。

在 rust 中,当 src 不能为空时,这样做很简单,我们可以这样做:

fn stream(dst: &mut [u8], src: &[u8]) {
    let mut block = [0u8, ..64];
    for (dstchunk, srcchunk) in dst.chunks_mut(64).zip(src.chunks(64)) {
        some_function_that_initializes_block(block);
        for (d, (&s, &b)) in dstchunk.iter_mut().zip(srcchunk.iter().zip(block.iter())) {
            *d = s ^ b;
        }
    }
}

但是,让我们假设我们希望能够模仿原始的 C 函数。然后我们想做类似的事情:

fn stream(dst: &mut[u8], osrc: Option<&[u8]>) {
    let srciter = match osrc {
        None => repeat(0),
        Some(src) => src.iter()
    };
    // the rest of the code as above
}

唉,这是行不通的,因为 repeat(0) 和 src.iter() 有不同的类型。然而,似乎不可能通过使用 trait 对象来解决这个问题,因为我们得到一个编译器错误,说 cannot convert to a trait object because trait 'core::iter::Iterator' is not object safe.(标准库中也没有对迭代器进行分块的函数)。

有什么好的方法可以解决这个问题,还是我应该在 match 语句的每个分支中复制代码?

【问题讨论】:

  • C 版本中的循环条件看起来很奇怪。 srclen 在循环中永远不会被修改,如果它最初大于 64,则使其无限。
  • 谢谢。编辑了帖子

标签: rust


【解决方案1】:

您可以调用通用内部函数,而不是在每个手臂中重复代码:

fn stream(dst: &mut[u8], osrc: Option<&[u8]>) {
    fn inner<T>(dst: &mut[u8], srciter: T) where T: Iterator<u8> {
        let mut block = [0u8, ..64];
        //...
    }

    match osrc {
        None => inner(dst, repeat(0)),
        Some(src) => inner(dst, src.iter().map(|a| *a))
    }
}

注意额外的 map 以使两个迭代器兼容 (Iterator&lt;u8&gt;)。


正如您所提到的,Iterator 没有内置的分块方式。让我们结合 Vladimir 的解决方案并在块上使用迭代器:

fn stream(dst: &mut[u8], osrc: Option<&[u8]>) {
    const CHUNK_SIZE: uint = 64;

    fn inner<'a, T>(dst: &mut[u8], srciter: T) where T: Iterator<&'a [u8]> {
        let mut block = [0u8, ..CHUNK_SIZE];
        for (dstchunk, srcchunk) in dst.chunks_mut(CHUNK_SIZE).zip(srciter) {
            some_function_that_initializes_block(block);
            for (d, (&s, &b)) in dstchunk.iter_mut().zip(srcchunk.iter().zip(block.iter())) {
                *d = s ^ b;
            }
        }
    }

    static ZEROES: &'static [u8] = &[0u8, ..CHUNK_SIZE];

    match osrc {
        None => inner(dst, repeat(ZEROES)),
        Some(src) => inner(dst, src.chunks(CHUNK_SIZE))
    }
}

【讨论】:

  • 谢谢,这似乎有效。只是一个小问题。写fn inner&lt;'a, T&gt;(dst: &amp;mut[u8], srciter: T) where T: Iterator&lt;&amp;'a [u8]&gt;和写fn inner&lt;'a, T: Iterator&lt;&amp;'a [u8]&gt;&gt;(dst: &amp;mut [u8], srciter: T)有什么区别?
  • 没有区别。它们的意思完全相同。引入where 语法是因为它通常会导致更易于理解的代码,并且它还允许指定关联类型的边界。
【解决方案2】:

不幸的是,不可能直接使用不同的迭代器或使用 trait 对象(最近已更改为禁止使用不适当的方法实例化 trait 对象,即在其签名中使用 Self 类型的那些)。但是,对于您的特定情况,有一种解决方法。只需使用枚举:

fn stream(dst: &mut [u8], src: Option<&[u8]>) {
    static EMPTY: &'static [u8] = &[0u8, ..64];  // '

    enum DifferentIterators<'a> {  // ' 
        FromSlice(std::slice::Chunks<'a, u8>),  // '
        FromRepeat(std::iter::Repeat<&'a [u8]>) // '
    }

    impl<'a> Iterator<&'a [u8]> for DifferentIterators<'a> {  // '
        #[inline]
        fn next(&mut self) -> Option<&'a [u8]> { // '
            match *self {
                FromSlice(ref mut i) => i.next(),
                FromRepeat(ref mut i) => i.next()
            }
        }
    }

    let srciter = match src {
        None => FromRepeat(repeat(EMPTY)),
        Some(src) => FromSlice(src.chunks(64))
    };

    let mut block = [0u8, ..64];
    for (dstchunk, srcchunk) in dst.chunks_mut(64).zip(srciter) {
        some_function_that_initializes_block(block);
        for (d, (&s, &b)) in dstchunk.iter_mut().zip(srcchunk.iter().zip(block.iter())) {
            *d = s ^ b;
        }
    }
}

不幸的是,这是很多代码,但作为回报,它比 C 版本更安全,更不容易出错。也可以对其进行优化以完全不需要repeat()

fn stream(dst: &mut [u8], src: Option<&[u8]>) {
    static EMPTY: &'static [u8] = &[0u8, ..64];  // '

    enum DifferentIterators<'a> {  // ' 
        FromSlice(std::slice::Chunks<'a, u8>),  // '
        AlwaysZeros
    }

    impl<'a> Iterator<&'a [u8]> for DifferentIterators<'a> {  // '
        #[inline]
        fn next(&mut self) -> Option<&'a [u8]> { // '
            match *self {
                FromSlice(ref mut i) => i.next(),
                AlwaysZeros => Some(STATIC),
            }
        }
    }

    let srciter = match src {
        None => AlwaysZeros,
        Some(src) => FromSlice(src.chunks(64))
    };

    let mut block = [0u8, ..64];
    for (dstchunk, srcchunk) in dst.chunks_mut(64).zip(srciter) {
        some_function_that_initializes_block(block);
        for (d, (&s, &b)) in dstchunk.iter_mut().zip(srcchunk.iter().zip(block.iter())) {
            *d = s ^ b;
        }
    }
}

【讨论】:

  • 感谢您的回答,它有效。我希望我能接受这两个答案,但我接受了另一个,因为它导致代码更少。
猜你喜欢
  • 2020-12-16
  • 1970-01-01
  • 1970-01-01
  • 2015-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
  • 2010-12-14
相关资源
最近更新 更多