在下面的防锈代码中,我试图更改数组的内容:
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
定义为mut
而example_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产生太多影响。
答案 0 :(得分:5)
fn iter(&self) -> Iter<T>;
fn iter_mut(&mut self) -> IterMut<T>;
他们返回的结构,Iter
和IterMut
,特别是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)
这里有两个正交的概念:
引用本身是否可变。这是i
和mut 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);
}
因为编译器无法保证i
和j
不指向内存中的相同位置。