If let mutable borrow

Hey,

New to rust here, what is the correct way to write the start_block function below, so that is able to pass a reference to the unwrapped block_start data member.?

use std::fmt::Write;

struct Output {
    indent_level: usize,
    data: String,
    block_start: Option<String>,
    block_end: Option<String>,
}

impl Output {
    fn new(block_start: &str, block_end: &str) -> Output {
        Output {
            indent_level: 0,
            data: String::new(),
            block_start: Some(block_start.to_owned()),
            block_end: Some(block_end.to_owned()),
        }
    }

    fn write(&mut self, s: &str) {
        write!(self.data, "{}", s);
    }

    fn inc_indent(&mut self) {
        self.indent_level += 1;
    }

    fn start_block(&mut self) {
        if let Some(block_start) = &self.block_start {
            self.write(block_start);
            self.inc_indent();
        }
    }
}

fn main() {
    let mut out = Output::new("{", "}");
    out.start_block();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:30:13
   |
29 |         if let Some(block_start) = &self.block_start {
   |                                    ----------------- immutable borrow occurs here
30 |             self.write(block_start);
   |             ^^^^^-----^^^^^^^^^^^^^
   |             |    |
   |             |    immutable borrow later used by call
   |             mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error

1 Like

&mut self is an exclusive lock over all fields of a struct, so a method taking &mut self is unable to receive anything else from the same struct as a parameter.

This is by design, because interface of a method is supposed to hide the implementation, so it counts what it declares, not what it actually does.

  • You can change it to a "static" method that takes data: &mut String without self.
  • You can use interior mutability (RefCell or Mutex) on data to be able to use a shared &self instead.
  • You can refactor the data to move data or block_start to a different struct.
  • You can add a separate write_block_start. It's fine to use both data and block_start in the same method.
4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.