Borrowing mutable self when calling methods on instance variables


#1

Hi all.

A few months ago I was implementing an LZW variant for compressing executables for an 8 bit platform, and needed to be able to output a few distinct bitstreams to cut down on byte boundary tests in the decoder (reading bitpairs should only require half as many checks).

I ran into an issue with not being able to pass a mutable reference to self to methods on instance variables. At the time, I worked around it by shifting the bit-writing code from the cursors into the encoder, and just switched on a stream index to determine which cursor to use, but I’m wondering if there was a better way, or if this is something that the borrow checker may become less strict about in the future now that the MIR is almost online.

Here’s a small test program that illustrates the issue:

#[derive(Debug)]
struct BitStreamCursor {
	bits_written: usize,
	bits_index: usize,
}

#[derive(Debug)]
struct Encoder {
	encoded_stream:Vec<u8>,
	bit_cursor:BitStreamCursor,
	pair_cursor:BitStreamCursor,
}

impl BitStreamCursor {
	fn new() -> BitStreamCursor {
		BitStreamCursor {
			bits_written:8,
			bits_index:0,
		}
	}
	fn push_bit(&mut self, parent:&mut Encoder, b:u8) {
		if self.bits_written==8 {
			self.bits_index=parent.encoded_stream.len();
			parent.encoded_stream.push(b<<7);
			self.bits_written=1
		}
		else {
			parent.encoded_stream[self.bits_index]+=b*(128>>self.bits_written);
			self.bits_written+=1
		}
	}
}
impl Encoder {
	fn new() -> Encoder {
		Encoder {
			encoded_stream: Vec::new(),
			bit_cursor:BitStreamCursor::new(),
			pair_cursor:BitStreamCursor::new(),
		}
	}
	fn push_byte(&mut self, b:u8) {
		self.encoded_stream.push(b);
	}
	fn push_bit(&mut self, b:u8) {
		self.bit_cursor.push_bit(self, b);
	}
	fn push_bitpair(&mut self, b:u8) {
		self.pair_cursor.push_bit(self, (b>>1));
		self.pair_cursor.push_bit(self, (b&1));
	}
}

fn main() {
	let mut e= Encoder::new();
	e.push_bit(1);
	e.push_bitpair(2);
	e.push_byte(42);
	println!("stream contents: {:?}",e);
}

#2

I don’t think your example as written would ever compile, since it allows aliased mutable references: in push_bit, you have a reference to the BitStreamCursor as &mut self, and can get another one via &mut parent.

In this example case, the solution is pretty easy as you only use parent.encoded_stream: pass the &mut Vec<u8> directly instead of a &mut Encoder:

self.bit_cursor.push_bit(&mut self.encoded_stream, b);

This is allowed since it mutably borrows two disjunct parts of self.


#3

As writ it indeed does not compile (hence the post).

But yes, your solution works! Thank you.

I missed that it was specifically the two aliases for the cursor that was the issue.


#4

I know, I just wanted to point out that this is an example that won’t compile even in the less-strict future :slight_smile:

Except if we get a notation for restricting borrows to subfields. But I can’t imagine a syntax that looks at least tolerable.


#5

Ah! Yes, that makes sense too. Thanks again.