在访问数组时以可变方式对其进行迭代

时间:2019-08-24 12:47:59

标签: rust

免责声明:Rust我还很陌生。

简化的用例

从到目前为止我所了解的关于Rust的最佳实践中,我了解到for elem in array {}的迭代比for i in 0..array.len(){}的迭代更受欢迎。

有什么方法可以可变地遍历数组,同时通过索引从数组中访问特定元素?

我的用例非常复杂,因此我编写了一个简单的斐波那契计算器来演示该问题:

let mut arr = vec![0;10];

arr[0] = 1;
arr[1] = 1;

for (i, elem) in arr.iter_mut().skip(2).enumerate() {
    *elem = arr[i-2] + arr[i-1];
}

println!("{:?}", arr);
  

错误[E0502]:无法借用arr作为不可变的,因为它也借来了可变的

这当然是有道理的,但是有办法解决吗?我的意思是,从程序员的角度来看,这段代码显然是安全的,因为我们从当前上下文中已经可变的数组中借用了不可变的变量,而不是直接地,而是通过迭代器。

当然,如果我通过遍历索引来实现它,那么它将起作用:

let mut arr = vec![0;10];

arr[0] = 1;
arr[1] = 1;

for i in 2..arr.len(){
    arr[i] = arr[i-2] + arr[i-1];
}

println!("{:?}", arr);
  

[1、2、3、5、8、13、21、34、55]

所以,我的问题是,有没有其他方法可以解决此问题,还是我必须使用第二个版本?


实际用例

此代码是为了演示我的用例,而不是单独执行任何操作。

let mut labels = vec![vec![0; width]; height];

for (y, row) in labels.iter_mut().enumerate() {
    for (x, label) in row.iter_mut().enumerate() {
        let label_left = {
            if x > 0 && some_condition() {
                Some(labels[y][x - 1]) // <== Fails
            } else {
                None
            }
        };

        let label_top = {
            if y > 0 && some_condition() {
                Some(labels[y - 1][x]) // <== Fails
            } else {
                None
            }
        };

        *label = some_function(label_left, label_right);
    }
}

使用基于2D索引的迭代来重写它很像我试图将C编程风格强制引入Rust,所以我不敢相信这是预期的方式。

1 个答案:

答案 0 :(得分:1)

一种更“实用”的方式来实现您的简化用例可以是:

fn main() {
    let mut arr = vec![0; 10];

    arr[0] = 1;
    arr[1] = 1;

    let arr: Vec<i32> = arr
        .iter()
        .skip(2)
        .scan((arr[0], arr[1]), |pair, _| {
            let (a, b) = *pair;
            let c = a + b;
            *pair = (b, c);

            Some(c)
        })
        .collect();

    println!("{:?}", arr);
}

但是,与索引迭代相比,这不一定更生锈或更容易阅读。就是说,如果您愿意沿着FP兔子洞走下去,那将是非常有益的。