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 не указывают на то же место в памяти.