这个循环如何变成惯用的 Rust?

How can this loop be turned into idiomatic Rust?

这是一个有效的 Rust 函数:

fn foo(src: &[u8]) -> Vec<u8> {
    let dst_len = (src.len() / 3) * 4;
    let mut dst = vec![0 as u8; dst_len];

    let mut si = 0;
    let mut di = 0;
    let n = (src.len() / 3) * 3;
    for _ in (0 .. n).step_by(3) {
        let v = bar(src[si], src[si+1], src[si+2]);
        dst[di+0] = baz(v, 0);
        dst[di+1] = baz(v, 1);
        dst[di+2] = baz(v, 2);
        dst[di+3] = baz(v, 3);
        si += 3;
        di += 4;
    }

    dst
}

它有效,但该循环看起来不像惯用的 Rust。它使用手动管理的索引索引到数组中,非常类似于 C 中的 for 循环。

有没有办法使用 Rust 迭代器实现相同的结果?我认为 chunked_exact 可以迭代 src,但是 dst 呢? zipsrc.chunked_exact 可以用什么迭代器分块写入 dst

什么是 "idiomatic" 可以见仁见智,但您可以使用更多迭代器方法,例如 zipchunks_exact_mut:

fn foo(src: &[u8]) -> Vec<u8> {
    let dst_len = (src.len() / 3) * 4;
    let mut dst = vec![0 as u8; dst_len];
    for (s, d) in src.chunks_exact(3).zip(dst.chunks_exact_mut(4)) {
        let v = bar(s[0], s[1], s[2]);
        d[0] = baz(v, 0);
        d[1] = baz(v, 1);
        d[2] = baz(v, 2);
        d[3] = baz(v, 3);
    }
    dst
}

我使用了 chunks_exactchunks_exact_mut 而不是 chunks,因为它保证切片具有请求的长度,如果需要它们可以单独使用。这似乎与您的原始代码相匹配,它将长度四舍五入为精确的步数。

我会同意的:

fn foo(src: &[u8]) -> Vec<u8> {
    src.chunks_exact(3)
        .map(|s| bar(s[0], s[1], s[2]))
        .flat_map(|v| (0..4).map(move |i| baz(v, i)))
        .collect()
}

或者如果您愿意:

fn foo(src: &[u8]) -> Vec<u8> {
    src.chunks_exact(3)
        .map(|s| bar(s[0], s[1], s[2]))
        .flat_map(|v| vec![baz(v, 0), baz(v, 1), baz(v, 2), baz(v, 3)])
        .collect()
}

不确定您是否发现其中之一更好。