迭代Option的内容或特定值

时间:2014-11-17 13:54:23

标签: rust

让我们说我们有以下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; }
    }
}

这是一个函数,它接受一个源和一个目标,而xors源具有一些​​值 功能计算。当source设置为NULL指针时,dst只是计算值。

生锈时,当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()有不同的类型,因此无法工作。但是,通过使用特征对象似乎无法解决这个问题,因为我们得到了一个编译器错误cannot convert to a trait object because trait 'core::iter::Iterator' is not object safe.(标准库中也没有用来组合迭代器的函数)。

有没有什么好方法可以解决这个问题,或者我应该只复制匹配语句每一部分的代码?

2 个答案:

答案 0 :(得分:2)

不幸的是,不可能直接使用不同的迭代器或使用特征对象(最近已经更改为禁止使用不适当的方法实例化特征对象,即在签名中使用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;
        }
    }
}

答案 1 :(得分:2)

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

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<u8>)。


正如您所提到的,Iterator没有内置的方法来进行分块。让我们整合弗拉基米尔的解决方案并使用迭代器而不是块:

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))
    }
}