Reputation: 771
I'm working on a Rust function that scans through the lines of a string and processes each character. The code below attempts to iterate through the lines of self.content and calls a mutable function self.handle_character on each character. However, I’m encountering a borrow checker error:
cannot borrow *self as mutable because it is also borrowed as immutable
pub fn run_scanner(&mut self) {
for (line_number, line) in self.content.lines().enumerate() {
let mut chars = line.chars();
while let Some(character) = chars.next() {
match self.handle_character(&character, &mut chars, line_number) {
Ok(_) => {},
Err(_) => break
}
}
}
}
fn handle_character(&mut self, current_char: &char, chars: &mut Chars, line_number: usize) -> Result<(), ()> {
What I've Tried
Collecting Lines to Vector: I tried collecting self.content.lines() into a Vec and then iterating over the vector, but the error persists.
Accessing Lines by Index: I attempted using self.content.lines().nth(line_number) within the loop to avoid holding an iterator borrow on self. However, this solution seems inefficient, especially with larger content, and I'm still encountering issues.
How can I resolve this borrowing conflict in Rust? Ideally, I'd like to iterate over self.content while allowing mutable access to self inside handle_character without running into the borrow checker error.
Any insights on efficient solutions or alternative approaches would be greatly appreciated!
Upvotes: 0
Views: 97
Reputation: 16925
First, let's try to find the origin of the problem.
In run_scanner()
, self.contents.lines()
references self.content
.
line.chars()
references line
(emitted by lines()
), thus references indirectly self.content
.
Passing chars
to handle_character()
if then like passing a reference to self.content
.
But, since this function can mutate the structure as a whole (via &mut self
), we are in a situation where self.content
is referenced via the chars
parameter while potentially mutated via the self
parameter.
Rust's borrow checker works exactly in order to prevent these ambiguous situations.
A solution (if suitable for your problem) would be to make chars
independent of self
in handle_character()
.
In the example below, we take content
out of the structure, work with it, then put it back into the structure.
The std::mem::take()
does not imply any copy; it's just pointer manipulation.
use std::str::Chars;
#[derive(Debug)]
struct Scanner {
content: String,
dummy: usize,
}
impl Scanner {
pub fn run_scanner(&mut self) {
let content = std::mem::take(&mut self.content);
// for (line_number, line) in self.content.lines().enumerate() {
for (line_number, line) in content.lines().enumerate() {
let mut chars = line.chars();
while let Some(character) = chars.next() {
match self.handle_character(
&character,
&mut chars,
line_number,
) {
Ok(_) => {}
Err(_) => break,
}
}
}
self.content = content;
}
fn handle_character(
&mut self,
current_char: &char,
chars: &mut Chars,
_line_number: usize,
) -> Result<(), ()> {
// something stupid, just in order to mutate self
self.dummy += chars.filter(|c| c == current_char).count();
Ok(())
}
}
fn main() {
let mut s = Scanner {
content: "something\nelse\n".to_owned(),
dummy: 0,
};
println!("before: {:?}", s);
s.run_scanner();
println!("after: {:?}", s);
}
/*
before: Scanner { content: "something\nelse\n", dummy: 0 }
after: Scanner { content: "something\nelse\n", dummy: 1 }
*/
Upvotes: 1
Reputation: 23319
Assuming self.content
is a String
, and assuming that handle_character
doesn't need to access self.content
directly, you can take self.content
out of self
while processing and put it back afterwards:
pub fn run_scanner (&mut self) {
let content = std::mem::take (&mut self.content);
for (line_number, line) in content.lines().enumerate() {
let mut chars = line.chars();
while let Some (character) = chars.next() {
match self.handle_character (&character, &mut chars, line_number) {
Ok(_) => {},
Err(_) => break
}
}
}
self.content = content;
}
⚠ Careful that you don't return in the middle of the loop without putting content
back!
Upvotes: 0