When working with Rust, sooner or later you'll encounter the "error[E0502]: cannot borrow ... as immutable because it is also borrowed as mutable". Sometimes it happens when we try to do something simple, something we routinely do in other languages. Let's check out some solutions to work around this problem.

Understanding the Problem

Let's suppose an application that contains two objects:


    pub struct Token {
        user_name: String,
        hash: Option<String>,
    }

    impl Token {
        fn new(user_name: String) -> Token {
            Token {
                user_name,
                hash: None,
            }
        }

        fn get_user_name(&self) -> &str {
            &self.user_name
        }

        fn get_hash(&mut self) -> &str {
            self.hash.get_or_insert_with(|| "ff0b14ba".to_string())
        }
    }

    pub struct Session<'a> {
        user_name: &'a str,
        hash: &'a str,
    }

    impl<'a> Session<'a> {
        fn login(user_name: &'a str, hash: &'a str) -> Session<'a> {
            Session { user_name, hash }
        }
    }

The Token object has two attributes, user_name and hash. The intention in this example is to suppose that getting the hash is an operation that requires some processing, so hash is obtained through the get_hash function, which internally has a lazy load trick, that's why it needs to be mutable.

To create the Session object requires user_name and hash, which we need to obtain through the Token object. Let's try to do this:


    // Code omitted

    fn main() {
        let mut token = Token::new("user".to_string());

        let user_name = token.get_user_name();
        let hash = token.get_hash();

        let session = Session::login(user_name, hash);

        println!("user_name: {} hash: {}", session.user_name, session.hash);
    }

But when compiling, we get the following error:


    error[E0502]: cannot borrow `token` as mutable because it is also borrowed as immutable
    --> src/main.rs:42:16
    |
 41 |     let user_name = token.get_user_name();
    |                     ----- immutable borrow occurs here
 42 |     let hash = token.get_hash();
    |                ^^^^^^^^^^^^^^^^ mutable borrow occurs here
 43 | 
 44 |     let session = Session::login(user_name, hash);
    |                                  --------- immutable borrow later used here

In Rust, we have to be careful when accessing references (borrowed &T) of a mutable object, as we end up "blocking" the object. In the error above, even accessing user_name through another variable, the compiler considers the token object as being referenced. This is because our get_hash method is mutable, but we're also accessing user_name which is immutable.

"Ok, I can't mix mutable and immutable references, I'll change everything to be mutable."

That won't work either. Rust only allows one mutable reference at a time.

Solutions

Solution 1: Clone the Value

The simplest solution is to clone the value so it's no longer a reference:


    fn main() {
        let mut token = Token::new("user".to_string());

        let user_name = token.get_user_name().to_string(); // Clone!
        let hash = token.get_hash();

        let session = Session::login(&user_name, hash);

        println!("user_name: {} hash: {}", session.user_name, session.hash);
    }

Solution 2: Reorder Operations

Sometimes simply reordering operations solves the problem:


    fn main() {
        let mut token = Token::new("user".to_string());

        let hash = token.get_hash().to_string(); // Get mutable first and clone
        let user_name = token.get_user_name();   // Then get immutable

        let session = Session::login(user_name, &hash);

        println!("user_name: {} hash: {}", session.user_name, session.hash);
    }

Solution 3: Use Interior Mutability

For more complex cases, you can use RefCell for interior mutability:


    use std::cell::RefCell;

    pub struct Token {
        user_name: String,
        hash: RefCell<Option<String>>,
    }

    impl Token {
        fn get_hash(&self) -> String {
            self.hash
                .borrow_mut()
                .get_or_insert_with(|| "ff0b14ba".to_string())
                .clone()
        }
    }

Conclusion

The borrow checker is one of Rust's most powerful features for preventing memory errors, but it can be frustrating at first. Understanding the rules and knowing the workarounds - cloning, reordering, or using interior mutability - will help you write safe and efficient Rust code.