4
votes

While checking out tutorials about Rust, I came across a problem with the borrow checker. The following code doesn't compile:

struct Car {
    model: String,
}

struct Person<'a> {
    car: Option<&'a Car>,
}

impl<'a> Person<'a> {
    fn new() -> Person<'a> {
        Person { car: None }
    }

    fn buy_car(&mut self, c: &'a Car) {
        // how to say that Person don't borrow the old car any longer?
        self.car = Some(c);
    }
}

fn main() {
    let civic = Car { model: "Honda Civic".to_string() };
    let mut ghibli = Car { model: "Maserati Ghibli".to_string() };
    let mut bob = Person::new();

    bob.buy_car(&ghibli);

    bob.buy_car(&civic);

    // error: cannot borrow `ghibli` as mutable because it is also borrowed as immutable
    let anything = &mut ghibli;
}

I understand that because of it's lexical nature Rust's borrow checker can't recognize that the borrow of ghibli has already ended.

But I would really like to know how to solve this problem the Rust way? Do I have to use Rc<T> or Box<T> in some way?

2

2 Answers

4
votes

The "Rust way" to tell the borrow-checker that a borrow ended is to introduce a new scope:

fn main() {
    let civic = Car{model: "Honda Civic".to_string()};
    let mut ghibli = Car{model: "Maserati Ghibli".to_string()};
    {
        let mut bob = Person::new();

        bob.buy_car(&ghibli);

        bob.buy_car(&civic);
    }
    let anything = &mut ghibli;
}

What you have to realize though, is that in your example, (and probably in most cases anyway) the borrow-checker was right.

Bob borrowed a reference to ghibli. It is still present at the end of the main method in his car field.

2
votes

This is a case where the static borrow rules don't work, since there's dynamic behaviour going on that you can't tell the compiler about.

You need to use Rc<T>, which can do the borrowing checks at runtime; or if you need mutable access then Rc<Cell<T>> or Rc<RefCell<T>>. Then as long as you access it in a dynamically safe way, all will be well.