Borrowing issue, trying to avoid redundancy


#1

I have the below code where I use the expression self.players[0] multiple times. Inside the first block is let us = &mut self.players[0] , which I tried moving outside of the block. That results in getting borrowing issues on self.sample_ndx since I’ve already borrowed from self.

	fn apply_connect_diagnosis( &mut self, turn : Turn ) {
		if let Some(ndx) = self.players[0].sample_ndx( turn.id ) {
			let us = &mut self.players[0];
			if us.samples[ndx].is_diagnosed() {
				self.samples.push( us.samples.remove( ndx ) );
			} else {
				let sample = &mut us.samples[ndx];
				sample.health = sample.rank * 10; //TODO: refer to the big table!!
				for i in 0..NUM_MOLECULE_TYPES {
					sample.cost[i] = sample.rank; //TODO: refer to the big table!!!
				}
			}
		} else if let Some(ndx) = self.sample_ndx( turn.id ) {
			self.players[0].samples.push( self.samples.remove(ndx) );
		}
	}

fn sample_ndx(&self, id : i32 ) -> Option<usize> { ... }

I find myself getting frustrated by this quite often. I’m used to creating these kind of aliases to avoid typing the same expression multiple times. In Rust however it ends up violating the borrowing rules quite often.

Am I missing some technique that would help me out? Is there some way I can create the alias without actually borrowing the value? This is one of the simpler examples I have, in other cases the redundancy has become a lot more excessive.


#2

There are a few ways to deal with this:

  1. Organize your code (and method calls) to avoid having a conflicting borrow in scope. This is not an issue if all you have is immutable borrows, but obviously comes into play once a mutable borrow comes into play.
  2. Make use of the fact the compiler understands field disjointness. This means instead of requiring/borrow all of self, borrow individual fields and pass them around to, e.g., associated functions.
  3. Instead of using methods that require a borrow of self, use a macro to deduplicate callers but avoid a borrow of self at the same time.

I’d say #1 and/or #2 are preferable.


Mutable iterator over field in self + mutable call
Approach to modify a struct using helpers within the struct?
#3

Yes, for #1 I learned that from a previous app and in this code the vast majority of my class uses an immutable borrow. It’s just these functions, called apply which need to have a mutable borrow, as they’re actually modifying the class (all of the values potentially).

For #2, would this require the caller of the functions select the properties, or is there some way the implementation functions themselves could do this? In this code the caller calls theState.apply( turn ) and has no idea what that does, thus couldn’t isolate the fields. apply in turn calls several functions like apply_connect_diagnosis.

For understanding, this class represents the state of a game, and the apply functions takes a Turn instance and modifies the state.


#4

The implementation functions indicate what info they need via parameters. So instead of them taking &mut self, for example, they would take concrete types they need. Those parameters can be abstracted into their own types, and even put behind something like a trait. The key bit is the caller knows what the callee wants, and is able to borrow fields individually to pass them along.

This may seem counter-intuitive to notions like “encapsulation” that we’ve all learned about at some point. However, you may find that this actually creates easier to reason about and maintain code; instead of passing a bunch of state into a black hole, and not knowing what the heck it does, you have a much narrower interface with clear borrowing distinction.