Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Immutable reference after mutable borrow

Tags:

rust

I run into similar problems related to ownership/borrowing every time I use Rust, so here is the simplest piece of code that illustrates my usual problems:

use std::cell::RefCell;

struct Res {
    name: String,
}

impl Res {
    fn new(name: &str) -> Res {
        Res {
            name: name.to_string(),
        }
    }

    // I don't need all_res to be mutable
    fn normalize(&mut self, all_res: &Vec<Res>) {
        // [...] Iterate through all_res and update self.name
        self.name = "foo".to_string();
    }
}

fn main() {
    let res = RefCell::new(vec![Res::new("res1"), Res::new("res2")]);

    for r in res.borrow_mut().iter_mut() {
        // This panics at runtime saying it's
        // already borrowed (which makes sense, I guess).
        r.normalize(&*res.borrow());
    }
}

After reading about RefCell I thought this would work. It compiles, but panics at runtime.

How do I reference a vector while iterating over the same vector? Is there any better data structure to allow me to do this?

like image 660
user3696012 Avatar asked Jun 17 '15 03:06

user3696012


People also ask

How do you borrow as mutable in Rust?

First, we change s to be mut . Then we create a mutable reference with &mut s where we call the change function, and update the function signature to accept a mutable reference with some_string: &mut String . This makes it very clear that the change function will mutate the value it borrows.

What is a mutable reference in Rust?

Back to Rust. A mutable reference is a borrow to any type mut T , allowing mutation of T through that reference. The below code illustrates the example of a mutable variable and then mutating its value through a mutable reference ref_i .

What is reference in Rust?

A reference of a variable is a pointer that leads to that variable. Rust uses the concept of ownership, which is associated with how references are used.

What is borrowing in Rust?

Rust supports a concept, borrowing, where the ownership of a value is transferred temporarily to an entity and then returned to the original owner entity.


1 Answers

Your program panics because you're trying to borrow the Vec mutably and immutably at the same time: this is not allowed.

What you need to do instead is wrap only the Strings in RefCell. This allows you to mutate the strings while iterating the Vec.

use std::cell::RefCell;

struct Res {
    name: RefCell<String>,
}

impl Res {
    fn new(name: &str) -> Res {
        Res {
            name: RefCell::new(name.to_string()),
        }
    }

    // I don't need all_res to be mutable
    fn normalize(&self, all_res: &Vec<Res>) {
        // [...] Iterate through all_res and update self.name
        *self.name.borrow_mut() = "foo".to_string();
    }
}

fn main() {
    let res = vec![Res::new("res1"), Res::new("res2")];

    for r in res.iter() {
        r.normalize(&res);
    }

    println!("{}", *res[0].name.borrow());
}
like image 100
Francis Gagné Avatar answered Oct 21 '22 12:10

Francis Gagné