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(¤t_state);
println!("a{} b{}", current_state.a, current_state.b);
//current_state.a = 17; // I don't want current state to be mutable here
}
}
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(¤t_state);
+ let current_state = ¤t_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:
Folding an iterator is very standard operation. (Easy to understand).
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.
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. ↩︎
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);
}
}
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).
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;
}
}
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];
}
}