Error: невозможно присвоить неизменяемому индексированному содержимому `i [..]`
В следующем коде ржавчины я пытаюсь изменить содержимое массива:
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
каким-то образом?
UPDATE:
Поэтому я обнаружил, что этот блок кода работает:
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.
Ответы
Ответ 1
Посмотрим на подписи двух методов: iter
и iter_mut
:
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
. Вы можете установить эти значения!
В качестве альтернативы, в вашем вопросе используются массивы, а не фрагменты, но нет ссылок на документацию для массивов (которые я мог бы найти быстро), а фрагменты достаточно близки к массивам, поэтому я использовал их для этого объяснения.
Ответ 2
Здесь есть два ортогональных понятия:
-
Является ли эта ссылка самой изменчивой. Это разница между i
и mut i
.
-
Могут ли данные, на которые он указывает, изменяться. То, что разница между .iter()
/&T
и .iter_mut()
/&mut T
.
Если вы используете C, это различие должно быть знакомым. Ваш исходный код создает изменяемые ссылки на неизменяемые данные или const char *
в C. Поэтому, когда вы можете назначить самому ссылку (i = ...
), вы не можете изменить данные, на которые он указывает (*i = ...
). Вот почему компилятор останавливает вас.
С другой стороны, ваш фиксированный код создает неизменяемые ссылки на изменяемые данные. Это char * const
в C. Это не позволяет вам назначать ссылку непосредственно, но она позволяет вам изменять базовый массив, поэтому он компилируется, как ожидалось.
Итак, почему у 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
не указывают на то же место в памяти.