How to fix this 'second mutable borrow occurs here' error


#1

Hello, I’m new to Rust. I’m trying to fix the following ‘second mutable borrow occurs here’ error.
Basically I wanted to write a function that uses httparse to parse a buffer as a Response and if it fails to parse it as Request. Also, I wanted to have a single httparse::Header array. Any suggestions?

Playground link: https://play.rust-lang.org/?gist=feadbc6338a08a18ab030affb1a3949d&version=stable&mode=debug

The error:

   Compiling tmp v0.1.0 (file:///home/oblique/scratchpad/rust/tmp)
error[E0499]: cannot borrow `*headers` as mutable more than once at a time
  --> src/main.rs:17:46
   |
10 |         let mut res = httparse::Response::new(headers);
   |                                               ------- first mutable borrow occurs here
...
17 |         let mut req = httparse::Request::new(headers);
   |                                              ^^^^^^^ second mutable borrow occurs here
...
24 | }
   | - first borrow ends here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.
error: Could not compile `tmp`.

To learn more, run the command again with --verbose.

My code:

extern crate httparse;

enum Http<'headers, 'buf: 'headers> {
    Res(httparse::Response<'headers, 'buf>),
    Req(httparse::Request<'headers, 'buf>),
}

fn parse_http<'b, 'h>(http: &'b [u8], headers: &'h mut [httparse::Header<'b>]) -> Result<Http<'h, 'b>, String> {
    {
        let mut res = httparse::Response::new(headers);
        if let Ok(_) = res.parse(&http) {
            return Ok(Http::Res(res));
        }
    }

    {
        let mut req = httparse::Request::new(headers);
        if let Ok(_) = req.parse(&http) {
            return Ok(Http::Req(req));
        }
    }

    Err("Failed to parse HTTP headers".into())
}

fn main() {
    let buf = b"GET /index.html HTTP/1.1\r\nHost: example.domain\r\n\r\n";
    let mut headers = [httparse::EMPTY_HEADER; 16];

    match parse_http(buf, &mut headers) {
        Ok(http) => {
            match http {
                Http::Res(_) => println!("response"),
                Http::Req(_) => println!("request"),
            }
        }
        Err(e) => eprintln!("{}", e),
    }
}

#2

This works, reusing the same borrow all the way through: https://play.rust-lang.org/?gist=1e85a5caa318955c6ec2e1de864da44f&version=stable&mode=debug

There might be a nicer way of doing it, not sure. You’re passing ownership of the sole mutable borrow to the response, so I think it stops existing once the response is dropped. I may be entirely wrong in my understanding of that though.

Edit: fixed playground link


#3

Thanks a lot!


#4

There’s a (relatively) well-known issue of a (conditional) return extending the borrow to outside the function. Here’s one github issue on it: https://github.com/rust-lang/rust/issues/40307

I thought NLL fixes this, but this playground suggests otherwise.

Maybe @pnkfelix can spare a few minutes to explain it.


#5

I think NLL + Polonius will fix that issue. (More info about Polonius in this blog post: http://smallcultfollowing.com/babysteps/blog/2018/06/15/mir-based-borrow-check-nll-status-update/ )


#6

Thanks @krdln. For the lazy, the link there does say the following about the conditional returns: