Am I forced to make this mutable or is there another way?

I want each instance of the struct to be immutable, but I don't know how to do that and still reuse the variable name for the next go round of the loop. Help?

struct State {
    a: i32,
    b: i32,
}

fn iterate(s: &State) -> State {
    State {
        a: s.a + 1,
        b: s.b + 1,
    }
}

fn main() {
    let mut current_state = State { a: 0, b: 100 };

    for _ in 0..100 {
        current_state = iterate(&current_state);
        println!("a{} b{}", current_state.a, current_state.b);

        //current_state.a = 17;  // I don't want current state to be mutable here
    }
}

You could shadow it with an immutable reference, after calling iterate:

        current_state = iterate(&current_state);
        let current_state = &current_state;  // <<<<< new

if you want a truly immutable variable for each iteration, you can use an iterator, e.g. with from_fn(), you can write this:

// clone is used to simplify this example
#[derive(Clone)]
struct State {
    a: i32,
    b: i32,
}

fn main() {
    // `mut` needed for closure capture mutation because I use `from_fn` in this example
    // alternatively, you can define a named iterator struct
    let mut state = State { a: 0, b: 100 };

    for current_state in std::iter::from_fn(move || {
        let next_state = iterate(&state);
        state = next_state.clone();
        Some(next_state)
    }).take(100) {
        println!("a{} b{}", current_state.a, current_state.b);

        // error: cannot assign to ... behind `&` reference
        //current_state.a = 17;  // I don't want current state to be mutable here
    }
}

if your goal is to prevent accidental modification to the variable, you can simply shadow the variable, e.g.:

    for _ in 0..100 {
        current_state = iterate(&current_state);
+       let current_state = &current_state;
        println!("a{} b{}", current_state.a, current_state.b);

+       // this now becomes a compile error too
        //current_state.a = 17;  // I don't want current state to be mutable here
    }

Another option besides std::iter::from_fn can also be std::iter::successors. E.g.

fn main() {
    let initial_state = State { a: 0, b: 100 };

    for current_state in std::iter::successors(Some(initial_state), |s| Some(iterate(s))).take(100)
    {
        println!("a{} b{}", current_state.a, current_state.b);

        // // vvvvv error[E0594]: cannot assign to `current_state.a`, as `current_state` is not declared as mutable
        // current_state.a = 17;
    }
}

Edit: Oh, this changes behavior somewhat, as it’ll now start with a0 b100 as the first output. Of course one could skip the first element to fix this discrepancy, though also slightly weird to me that the initial state is completely skipped in the first place in the original program. But that might be intended behavior, so I’m just pointing out the subtlety.

Similar idea to what others showed, but one using Iterator::fold:

#!derive([Debug, Clone, Copy])
struct State {
    a: i32,
    b: i32,
}

impl State {
    fn next_state(self) -> Self {
        Self {
            a: self.a + 1,
            b: self.b + 1,
        }
    }

    fn print(&self) {
        println!("a{} b{}", self.a, self.b);
    }
}

impl Default for State {
    fn default() -> Self {
        Self { a: 0, b: 100 }
    }
}

fn main() {
    let final_state = (0..100).into_iter().fold(State::default(), |acc, _| {
        let next = acc.next_state();
        next.print();
        next
    });

    final_state.print();
}

I find it more elegant because:

  1. Folding an iterator is very standard operation. (Easy to understand).
  2. Closure passed to fold does not capture anything from the environment[1]. It just operates on copies of State. This essentially sidesteps your problem of preventing state mutation altogether.

  1. Closure passed to fold is actually FnMut, so in theory one could keep the state outside the iterator and access/mutate it inside fold. This however would require intentional setup, which is very hard to create by mistake. The result would stick out like a sore thumb, which would be quickly called out by even slightly competent Rust programmer during code review. ↩︎

Itertools has the fittingly-named iterate exactly for this purpose:

let initial = State { a: 0, b: 100 };
for current_state in itertools::iterate(initial, iterate).take(100) {
    // ...
}

itertools::iterate is almost identical to std::iter::successors, right?

Yes, except it always produces an infinite iterator, so has a simpler type signature without Option

No more mut for State, even the initial one:

fn main() {
    let current_state = State { a: 0, b: 100 };

    let mut state_store = Some(current_state);
    
    for _ in 0..100 {
        let current_state = iterate(&state_store.as_ref().unwrap());
        
        println!("a{} b{}", current_state.a, current_state.b);

        //current_state.a = 17;  // I don't want current state to be mutable here
        
        state_store = Some(current_state);
    }
}

Or, with a simple store:

mod store {
    pub struct Store<T> {
        item: T
    }
    impl<T> Store<T> {
        pub fn new(item: T) -> Self {
            Self { item }
        }
        pub fn replace(&mut self, item: T) {
            self.item = item;
        }
    }
    impl<T> std::ops::Deref for Store<T> {
        type Target = T;
        fn deref(&self) -> &T {
            &self.item
        }
    }
}


fn main() {
    let current_state = State { a: 0, b: 100 };

    let mut store = store::Store::new(current_state);
    
    for _ in 0..100 {
        let current_state = iterate(&store);
        
        println!("a{} b{}", current_state.a, current_state.b);

        // current_state.a = 17;  // I don't want current state to be mutable here
        // store.item is not accessible
        
        store.replace(current_state);
    }
}

No need for the Option.

This pattern also works with moves...[1]

fn main() {
    let mut state_store = State { a: 0, b: 100 };
    for _ in 0..100 {
        let current_state = iterate_by_value(state_store);
        println!("a{} b{}", current_state.a, current_state.b);

        //current_state.a = 17;  // I don't want current state to be mutable here
        state_store = current_state;
    }
}

...wherein the state_store is uninitialized (and unusable) between the first and last lines of the loop (and the only mutation happening is reinitialization of state_store).


  1. let's ignore that this type could trivially be Copy ↩︎

Ah, yes, Thx. And that gives me an idea for which I've never used drop before:

fn main() {
    let mut state_store = State { a: 0, b: 100 };

    for _ in 0..100 {
        let current_state = iterate(&state_store);
        drop(state_store);

        println!("a{} b{}", current_state.a, current_state.b);

        // current_state.a = 17;  // I don't want current state to be mutable here
        
        state_store = current_state;
    }
}

Things I never have used macro hygiene before:

fn main() {
    let mut state = State { a: 0, b: 100 };

    for _ in 0..100 {
        macro_rules!__[[$state:ident]=>[
            state = $state; // defined before next line, can still reference outer `state`
        ]];
        let state = iterate(&state); // shadows mutable outer `state`

        println!("a{} b{}", state.a, state.b);

        __![state];
    }
}