After a lot of struggle getting used to borrowing, references, ownership etc. and trying to follow the compiler's suggestions how to fix certain issues, I ended up with the following pair of functions in my first bit of Rust code.
fn adjust_culture(mut cooperating_families: Vec<&mut Family>) {
for f in cooperating_families.iter_mut() {
mutate_culture(f);
}
let family: Option<&&mut Family> = cooperating_families.choose(&mut rand::thread_rng());
match family {
None => {},
Some(f) => {
let target: Culture = f.culture;
for f2 in cooperating_families {
f2.culture = target;
}
}
}
}
fn mutate_culture(family: &mut Family) {
if family.seasons_till_next_mutation > 0 {
family.seasons_till_next_mutation -= 1;
}
if family.seasons_till_next_mutation == 0 {
let i: u8 = rand::thread_rng().gen_range(0, culture_dimensionality);
family.culture ^= 1 << i;
}
}
The second one makes sense to me, and preventing it from gaining additional borrowing decoration was a major point getting me to this solution. But adjust_culture
confuses me: Why did the compiler suggest to add the mut
before cooperating_families
in the function's parameter definition?
I thought I would just have to pass a mutable binding to access the fields of the individual families, but with another function
fn extract_resources(patch: Patch, group: Vec<&mut Family>, total_labor_here: usize) -> KCal {
let labor: u8 = group.iter().map(|f| {f.effective_size as u8}).sum();
let resources_extracted = resources_from_patch(
patch, labor as usize, total_labor_here - labor as usize,
false);
for family in group {
family.stored_resources +=
resources_extracted * family.effective_size as f32 / labor as f32
}
return resources_extracted
}
Rust told me that group
does not need to be mutable, and now I'm quite confused.
Which bit of adjust_culture
has a chance to change the value of cooperating_families
instead of just the objects inside it? How do I avoid that from happening – my mental idea of the semantics says is should not happen?