Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring messes up mutable borrow - why?

Tags:

rust

lifetime

I'm trying to understand why does the following refactoring result in an error, even though it should effectively have the same behaviour:

Before:

fn req_handler(req: &mut Request) -> IronResult<Response> {
    let pool = req.get::<Read<Database>>().ok().expect("database component not initialised");
    let connection = pool.get().unwrap();

    let maybe_id = req.extensions.get::<Router>().unwrap().find("id");
    ...

After:

pub fn get_pool_connection<'a, 'b, 'c>(req: &'a mut Request<'b, 'c>) -> PooledConnection<'a, PostgresConnectionManager> {
    let pool = req.get_ref::<Read<Database>>().ok().expect("database component not initialised");
    pool.get().unwrap()
}
fn req_handler(req: &mut Request) -> IronResult<Response> {
    let connection = get_pool_connection(req);
    let maybe_id = req.extensions.get::<Router>().unwrap().find("id");

This results in error:

src/main.rs:64:20: 64:34 error: cannot borrow `req.extensions` as immutable because `*req` is also borrowed as mutable
src/main.rs:64     let maybe_id = req.extensions.get::<Router>().unwrap().find("id");
                                  ^~~~~~~~~~~~~~
src/main.rs:62:42: 62:45 note: previous borrow of `*req` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*req` until the borrow ends
src/main.rs:62     let connection = get_pool_connection(req);
                                                        ^~~
src/main.rs:76:2: 76:2 note: previous borrow ends here
src/main.rs:61 fn req_handler(req: &mut Request) -> IronResult<Response> {
...
src/main.rs:76 }

So the problem is that get_pool_connection borrows the request and gives back connection which prevents further req use. But why does this happen? req is guaranteed to use at least the same lifetime as the returned PooledConnection. It's not moved either, it was just passed as &mut. So what prevents the request from being used?

And why does the error say that *req was borrowed, when both local req and the function parameter are references?

(relevant docs: Request, Pool)

like image 746
viraptor Avatar asked Aug 16 '26 00:08

viraptor


1 Answers

That's actually exactly the meaning of the lifetime annotations. If you have a function having this prototype:

fn get_bar<'a>(&'a Foo) -> Bar<'a> { ... }

It means that the Bar object returned owns a lifetime tied to the one of the Foo object. As a consequence:

  • the Bar objects borrows the Foo object as long as it is alive
  • the Bar object is not allowed to outlive the Foo object.

In your case, connection is of type PooledConnection<'a, ...>, where 'a is the lifetime defined in &'a mut req, it thus is considered as a mutable borrow of req.

It worked before the refactoring, because the lifetime of connection is actually linked to the lifetime of pool, which didn't borrow req as it does not hold any lifetime parameter.

As your refactoring forces connection to borrow req, which was not needed before, maybe it is not an appropriate refactoring.

like image 178
Levans Avatar answered Aug 19 '26 01:08

Levans