错误:无法分配给不可变索引内容`i [..]`

时间:2015-01-26 22:36:48

标签: rust

在下面的防锈代码中,我试图更改数组的内容:

    let mut example_state = [[0;8]; 2];
    for mut i in example_state.iter() {
        let mut k = 0;
        for j in i.iter(){
            i[k] = 9u8;
            k +=1
        }
    }

但是我收到错误消息:

src/main.rs:18:13: 18:23 error: cannot assign to immutable indexed content `i[..]`
src/main.rs:18             i[k] = 9u8;
我感到困惑的是因为我将i定义为mutexample_state也是可变的。

我也不知道这是否是更改数组内容的最佳方法 - 我是否需要计数器k或者我可以简单地以某种方式使用迭代器j

更新 所以我发现这段代码可以工作:

let mut example_state = [[n;8]; 2];
for i in example_state.iter_mut() {
    for j in i.iter_mut(){
        *j = 9u8;
    }
}

但我很欣赏他们之间存在差异的一些解释,iter_mut并没有对Google产生太多影响。

2 个答案:

答案 0 :(得分:5)

让我们看一下iteriter_mut两种方法的签名:

fn iter(&self) -> Iter<T>;
fn iter_mut(&mut self) -> IterMut<T>;

他们返回的结构,IterIterMut,特别是Iterator的实现:

// Iter
type Item = &'a T
// IterMut
type Item = &'a mut T 

这些是关联类型,但基本上在这种情况下,它们指定了调用Iterator::next的返回类型。当您使用iter时,即使它位于可变变量上,您也要求不可变引用的迭代器为类型T&T)。这就是为什么你无法改变它们的原因!

当您切换到iter_mut时,Iterator::next的返回类型为&mut T可变引用返回类型T。您可以设置这些值!

顺便说一句,您的问题使用了数组,而不是 slices ,但是没有数组的文档链接(我可以快速找到),并且切片很接近足够数组,所以我用它们来解释。

答案 1 :(得分:4)

这里有两个正交的概念:

  • 引用本身是否可变。这是imut i之间的区别。

  • 指向的数据是否可变。这是.iter() / &T.iter_mut() / &mut T之间的差异。

如果你使用C,这种区别应该是熟悉的。您的初始代码在C中创建可变引用不可变数据const char *。因此,虽然您可以分配给引用本身(i = ...),您无法修改它指向的数据(*i = ...)。这就是编译器阻止你的原因。

另一方面,您的固定代码会将不可变引用创建为可变数据。这是C中的char * const。这不允许您为引用本身分配,但它确实允许您修改基础数组,因此它按预期编译。


那么为什么Rust有一个单独的.iter().iter_mut()?因为在Rust中,您可以根据需要将尽可能多的&T用于结构,但只能通过单个&mut T进行修改。换句话说,可变引用是唯一的,而不是alias

同时拥有.iter().iter_mut()可供您选择。一方面,您可以同时在作用域中包含任意数量的不可变迭代器,所有迭代器都指向同一个数组。这是一个愚蠢的例子,它同时向前和向后迭代:

for i, j in array.iter().zip(array.iter().rev()) {
    println!("{} {}", i, j);
}

但是如果你想要一个可变的迭代器,你必须保证引用永远不会别名。所以这不起作用:

// Won't compile
for i, j in array.iter_mut().zip(array.iter_mut().rev()) {
    println!("{} {}", i, j);
}

因为编译器无法保证ij不指向内存中的相同位置。