How to bypass "cannot borrow as mutable more than once"?


#1

Hi there!

I am really new to Rust, but I would love to use it as a web server.

I encounter issues regarding mutable reference borrowing (using Iron, but my issue is general).

The action called by Iron after routing is as follows:

pub fn hello(req: &mut Request) -> IronResult<Response>

I would like to call a centralized code in an struct of mine, that checks the request headers and returns a response in case of error (and I won’t duplicate this code in each and every action I will have to write)

This is my method:

pub fn init(&mut self, req: &mut Request)

I call it (inside “hello()”) like this:

req.extensions.get_mut::<App>().unwrap().init(req);

But Rust does not like it and it tells me:

error: cannot borrow `*req` as mutable more than once at a time
note: previous borrow of `req.extensions` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `req.extensions` until the borrow ends
note: previous borrow ends here (end of function)

How am I supposed to do this? I have spent hours reading the doc and browsing the web, but to no avail. Can’t I just call a function and give it my reference? I thought I could, since the init() method ends and returns the (temporary) ownership of the reference back to the hello() method… It means that only one method can actually use that reference at a given time. Right?

Any tips to help me better understand what I am doing wrong and the proper way to do it, would be much appreciated!

Sincerely,


#2

The problem is that you’re trying to lend req to init while it is still lent to get_mut. If you look at the signatures, you’ll see:

fn get_mut<K>(&mut self) -> Option<&mut K::Value>

Meaning the result depends on borrowing self, which in this case is req.extensions, which in turn means the entirety of req is borrowed.

I don’t know Iron well enough to suggest what to do here, but fundamentally, you really only have one option: redesign the code to not need mutable aliasing.

Do you really need all of req? Can you instead extract the information you actually need first and pass that in?

let init_stuff = App::extract_init_stuff(req);
req.extensions.get_mut::<App>().unwrap().init(init_stuff);

Maybe you could replace App with Option<App>, which would allow you to remove the value from extensions, call methods on it, then put it back.

let app = req.extensions.get_mut::<Option<App>>().unwrap().take().unwrap();
app.init(req);
*req.extensions.get_mut::<Option<App>>().unwrap() = Some(app);

#3

Thank you for the reply!

However, I do not understand. How is it possible that “req is still lent to get_mut” whereas the get_mut() function call is long terminated when I call app.init()?

What I do is store my app instance in req.extensions in my BeforeMiddleware:

fn before(&self, req: &mut Request) -> IronResult<()>
{
    req.extensions.insert::<App>(App::new());
    Ok(())
}

It allows me to have one App struct instance for each new incoming request. Which is then made available in the matching route/action:

pub fn hello(req: &mut Request) -> IronResult<Response>
{
    let app = req.extensions.get_mut::<App>().unwrap();
    app.init(req, false);
}

Obviously the “previous borrow” that occurs during get_mut is clearly over, right? Why is it continuing up until the hello() body’s end?

Is there a way to explicitly tell the compiler that the previous borrow is over, and that we can borrow again? Or to temporarily lend “borrowship” to another function call, because obviously the two can’t overlap (modify the same reference at the exact same time: they do it sequentially) and therefore that security is kind of useless in that precise case?

I could indeed try to modify App::init() in order to take as parameters some data passed by copy instead of a mut ref to the request, but I would feel very uneasy to do so, because I don’t see a reason why I should have to.

Even if I try to change my init() parameter as immutable ref, it does not work :frowning:

pub fn init(&mut self, req: &Request)
==> error: cannot borrow `*req` as immutable because `req.extensions` is also borrowed as mutable

This is really frustrating, I don’t have the sensation to do anything wrong here. I guess it is the one feature of Rust that takes accomodating to…

PS: I tried your suggestion and Rust complains that “the trait typemap::Key is not implemented for the type `core::option::Option”


#4

No, it isn’t. That’s what I’m trying to say: you’re trying to alias the data, and Rust is stopping you.

Here’s the code in question:

req.extensions.get_mut::<App>().unwrap().init(req);

Here’s what it effectively expands to. Note that I’m using 'x lifetimes to track how borrows are tied to one another; the way I’m using them here isn’t valid.

pub fn hello<'a>(req: &'b mut Request) -> IronResult<Response> {
    let temp: &'a mut TypeMap     = &'a mut req.extensions;
    let temp: Option<&'a mut App> = temp.get_mut::<App>();
    let temp: &'a mut App         = temp.unwrap();
    temp.init(req);
    // ...
}

So on that last line, you have a mutable borrow to an App, which is derived from a mutable borrow to a TypeMap, which is derived from the mutable borrow to Request. Thus, you’re trying to use req in two places simultaneously.

Changing App::init to take a &Request doesn’t help because you’d still be aliasing a mutable pointer. The rules here are simple: any number of immutable references or exactly one mutable reference and nothing else.

The only option is to either not need multiple references to the same thing, or to break the chain of references by copying or moving something.

Actually, it just occurred to me: why are you inserting App before you initialise it? Why don’t you just create and initialise App in the hello method, then insert it into extensions? Though, without knowing what you’re actually trying to do, I can’t say if that’s actually applicable or not.


#5

Oooooh I actually understand now!! What you tell me is it’s because my app item is part of the data of req.extensions, and this causes my reference to req to be still “active”. I understand now, thank you for this kind explanation!

I also tried your suggestion, because it makes sense, and it works!! So I’m leaving it like that for now :smile:

// Init app, check headers, check authorization token, init state
let app = App::init(req, false);
match app
{
    // App can return either an Iron Response in case of error
    Err(res) => { Ok(res) }
    // Or an instance of App, in case of success
    Ok(app) =>
    {
        // Keep a reference to app so that we can access it in the AfterMiddleware
        // (that will take care of specifying content-type, language, add headers, ...)
        req.extensions.insert::<App>(app);
        Ok(Response::with((status::Ok, "Hello world")))
    }
}

What pains me though is to have all this code in each and every action/route. I tried to create the app instance in the BeforeMiddleware because I like my code to be factorized, and duplicating it (like above) makes me want to cry.

I come from a PHP background. I am actually trying to migrate my (working) app to Rust, because Rust seems sooo nice! But in PHP I would have been able to factorize everything in the “BeforeMiddleware”, including sending an error response if need be, which would make every route/action much more cleaner and simpler.

I did not find an answer: is it possible to say “I don’t need that borrowed reference anymore, please have it back so that I can borrow it again but in another place” ? Like, for example, a “delete myref”? Or “myref.give_back()” I dunno?

Also I was surprised that the above code works, but this code fails:

req.extensions.insert::<App>(App::init(req, false));

Yes I don’t have error management anymore but it’s just for sake of argument (assuming init() only returns an app instance): it seems like the code is blindly analyzed from left to right, instead of first analyze what’s inside the arguments, and then analyze the function call (which we get by using a temporary variable, but makes our code 2 lines long instead of a one-liner)

Is there a reason for this?

Thank you for all your help Daniel,


#6

Currently, only by allowing the borrow to fall out of scope:

let mut n = 0;
{
    let x = &mut n;
    // this is an error; x is still "alive"
    // let y = &mut n;
}
// this is OK because x is gone now
let z = &mut n;

No, you can’t make it go away earlier. No, really. Yes, it sucks; there are changes coming to make this better c.f. “non-lexical borrows” (this comes up fairly frequently).

Because the compiler is borrowing req for the call to insert first, and then attempting to pass the borrow to App::init. Turns out that order-of-evaluation in Rust is really important when borrows enter the picture. Sometimes, you just have to use a temporary variable.

Beyond “that’s how the compiler is implemented”? I don’t believe so; I know there was a discussion recently on actually defining what the order of evaluation is supposed to be, but even that has proved tricky in the presence of borrowing.


#7

Right now, all borrows are based on lexical scope. There are plans to change this in the future, but the only way to remove a borrow is to cause the reference to go out of scope.


#8

Mmmh nice, so I just have to create a scope using braces right? Thanks for
the input, I’ll keep it in mind for later!


#9

Thank you for that complete answer Daniel, it helps a lot!