This question already has answers here:
What's the difference between placing "mut" before a variable name and after the ":"?
(4 answers)
Why does Rust allow mutation through a reference field using an immutable binding?
(1 answer)
Closed 2 years ago.
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?
Related
This question already has an answer here:
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
(1 answer)
Closed 3 months ago.
I am implementing a cache which tries a lookup in a table, if that fails it tries a simple method to get the value, and if that fails, it goes and computes multiple new entries in the cache. The Entry system seems specifically designed for the first half of this, but I cannot get the borrow checker to allow me to complete the last half.
use std::collections::HashMap;
fn main() {
let mut cache = Cache { cache: HashMap::new() };
println!("{}", cache.get_from_cache(10));
}
struct Cache {
cache: HashMap<u32, String>
}
impl Cache {
fn get_from_cache<'a>(&'a mut self, i: u32) -> &'a String {
match self.cache.entry(i) {
std::collections::hash_map::Entry::Occupied(entry) => return entry.into_mut(),
std::collections::hash_map::Entry::Vacant(entry) => {
// Some values have an easy way to be computed...
if i == 5 {
return entry.insert("my string".to_string())
}
}
}
// Neither look-up method succeeded, so we 'compute' values one-by-one
for j in 1..=i {
self.cache.insert(j, "another string".to_string()); // Borrow checker fails here
}
self.cache.get(&i).unwrap()
}
}
The problem is that the Entry from self.cache.entry(i) borrows self.cache for the entire lifetime 'a even though I no longer need it at the point that I attempt to do self.cache.insert.
One solution to this (I think) would be if there were a way to turn my reference to the Entry into a reference to its HashMap and then insert through that reference. However, I see no way to do this with the entry interface. Is there some way to achieve this? Or to otherwise satisfy the borrow checker?
This is easily fixed by separating inserting values from returning the final result. You can first make sure the value is in the cache or insert it with some strategy if not, and then return the new value (now guaranteed to be in the HashMap):
fn get_from_cache<'a>(&'a mut self, i: u32) -> &'a String {
// handle inserting the value if necessary:
match self.cache.entry(i) {
std::collections::hash_map::Entry::Occupied(entry) => (),
// Some values have an easy way to be computed...
std::collections::hash_map::Entry::Vacant(entry) if i == 5 => {
entry.insert("my string".to_string());
}
// ... others aren't
std::collections::hash_map::Entry::Vacant(entry) => {
for j in 1..=i {
self.cache.insert(j, "another string".to_string());
}
}
}
// The value is now definitely in `self.cache` so we can return a reference to it
&self.cache[&i]
}
This question already has answers here:
How can I port C++ code that uses the ternary operator to Rust?
(3 answers)
How do I idiomatically convert a bool to an Option or Result in Rust?
(4 answers)
Closed 1 year ago.
In C, there is the conditional operator on the form int x = foo > bar ? 1 : 0;. According to this issue, that seems to be something the Rust developers do not want to implement. If one wants to be concise: Is there a Rust equivalent/shorthand version to do the same?
struct Config {
timeout: Option<u32>,
}
impl Config {
// C-style conditional operator
pub fn new(timeout: u32) {
Config {
timeout == 0 ? None : Some(timeout)
}
}
// My attempted Rust approach
pub fn new(timeout: u32) {
let mut t = None;
if timeout != 0 {
t = Some(timeout);
}
Config { timeout: t }
}
}
fn main() {
let config = Config::new(0);
}
I guess I have two problems with the Rust approach as of now: 1) I need to create a new variable which is very hard to name anything meaningful and 2) the code is less concise than the C-style conditional operator (though perhaps more explicit).
This question already has answers here:
Do mutable references have move semantics?
(1 answer)
What are non-lexical lifetimes?
(1 answer)
Closed 2 years ago.
I have the following code that compiles and runs without any issues -
#[derive(Debug)]
pub struct A<'a> {
field: &'a mut String
}
fn main() {
let mut b = "Hello".to_owned();
let a = A { field: &mut b };
let c = A { field: a.field };
c.field.push('+');
a.field.push('+');
//println!("{:?}", c);
// println!("{:?}", a);
}
But I am confused as to why this code is working, or even compiling, for the following reasons -
It seems like we are holding two mutable references to the same object b. This is evident since both c.field.push('+'); and a.field.push('+'); succeed.
Why does this statement - let c = A { field: a.field };, not move field out of A (since mutable references are not copyable) and invalidate A?
This question already has answers here:
Temporarily move out of borrowed content
(3 answers)
How can I swap in a new value for a field in a mutable reference to a structure?
(2 answers)
Closed 4 years ago.
I have an immutable data structure, and an update function that takes ownership of the data structure and returns a new data structure:
enum Immutable {
Item(i32)
}
fn update(imm: Immutable) -> Immutable {
match imm {
Immutable::Item(x) => Immutable::Item(x + 1)
}
}
I need to store the data structure in a mutable field of a container:
struct State {
item: Immutable
}
I want to write an imperative update function for State that calls the function updater:
fn update_mut(st: &mut State) -> () {
let mut owned = Immutable::Item(42); // junk
std::mem::swap(&mut st.item, &mut owned);
st.item = update(owned);
}
This code works, but it seems sily to use mem::swap and allocate a junk object. I would really like to write:
fn update_mut_type_error(st: &mut State) -> () {
let mut owned = Immutable::Item(42); // junk
std::mem::swap(&mut st.item, &mut owned);
st.item = update(st.item); // type error
}
Is there any way to address this? Or, do I have to use mem::swap here, even though it seems spurious.
Example on Rust Playground
As Sven points out, this question is answered here:
http://smallcultfollowing.com/babysteps/blog/2018/11/10/after-nll-moving-from-borrowed-data-and-the-sentinel-pattern/
This question already has an answer here:
Why does removing return give me an error: expected type `()` but found type
(1 answer)
Closed 6 years ago.
I have a world object:
use rand::{thread_rng, Rng};
use super::world::World;
pub struct Worlds {
worlds: Vec<World>
}
impl Worlds {
pub fn new(world: Vec<World>) -> Worlds {
Worlds { worlds: world }
}
pub fn get_random_world(&self) -> World {
let mut rng = thread_rng();
if self.worlds.len() > 0 {
let world_index: usize = rng.gen_range(0, self.worlds.len());
self.worlds[world_index]
}
self.worlds[0]
}
}
The structure Worlds takes a vector of structures called World (I can post that code if you need it).
get_random_world is supposed to return a World structure, and does with self.worlds[world_index], but apparently it expects a ().
I'm lost; I told it what to return, it returns that but it expects something different?
|
20 | self.worlds[world_index]
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found struct `game::world::world::World`
|
= note: expected type `()`
= note: found type `game::world::world::World`
I'm not sure what to do at this point.
Your if block doesn't return anything.. flow will fall through here:
if self.worlds.len() > 0 {
let world_index: usize = rng.gen_range(0, self.worlds.len());
self.worlds[world_index]
} // <--- ... this drops down
self.worlds[0]
Here is a simple reproduction of your issue: Playground link
There are two ways you can fix this. First, you can explicitly return in the conditional (View it on the playground):
if self.worlds.len() > 0 {
let world_index: usize = rng.gen_range(0, self.worlds.len());
return self.worlds[world_index]; // Explicitly return out of here
}
self.worlds[0]
The more "idiomatic" approach I guess would be to add an else block, so that the value the conditional evaluates to is actually what the function returns (View it on the playground):
if self.worlds.len() > 0 {
let world_index: usize = rng.gen_range(0, self.worlds.len());
self.worlds[world_index]
} else {
self.worlds[0]
}
Now, no matter what logical branch the code takes, the expression that results will be returned from the function.