Lifetime and borrowing issue with mut self methods

This is a minimal example of my case.
I want to call method_a and on specific failure call method_b.
I can not figure out what I'm doing wrong.

Playground link: Rust Playground

use std::io::{ErrorKind, Result};

struct Foo {}

struct Bar<'f> {
    foo: &'f mut Foo,
}

impl Foo {
    fn method_a<'f>(&'f mut self) -> Result<Bar<'f>> {
        Err(ErrorKind::WouldBlock.into())
    }

    fn method_b<'f>(&'f mut self) -> Result<Bar<'f>> {
        Ok(Bar {
            foo: self,
        })
    }
}

fn method_a_or_b<'f>(foo: &'f mut Foo) -> Result<Bar<'f>> {
    match foo.method_a() {
        Ok(bar) => Ok(bar),
        Err(ref e) if e.kind() == ErrorKind::WouldBlock => foo.method_b(),
        Err(e) => Err(e),
    }
}

fn main() {
    let mut foo = Foo {};
    let _bar = method_a_or_b(&mut foo).unwrap();
}

The error message is the following:

error[E0499]: cannot borrow `*foo` as mutable more than once at a time
  --> src/main.rs:24:60
   |
21 | fn method_a_or_b<'f>(foo: &'f mut Foo) -> Result<Bar<'f>> {
   |                  -- lifetime `'f` defined here
22 |     match foo.method_a() {
   |           --- first mutable borrow occurs here
23 |         Ok(bar) => Ok(bar),
   |                    ------- returning this value requires that `*foo` is borrowed for `'f`
24 |         Err(ref e) if e.kind() == ErrorKind::WouldBlock => foo.method_b(),
   |                                                            ^^^ second mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.
error: could not compile `lifetime-issue`.

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

The current borrow checker has trouble with certain types of borrows that get returned from the current function to its caller.

The next-generation borrow checker, Polonius, fixes these problems. For example, the test case above compiles successfully using the nightly Rust toolchain with the experimental rustc -Z polonius option.

[EDIT: I tried to provide a workaround that compiles on stable Rust but I couldn't come up with a decent one. I'll take another look tomorrow unless somebody else gets to it first.]

Since this is a limitation of the current borrow checker, I did a workaround with unsafe:

fn method_a_or_b<'f>(foo: &'f mut Foo) -> Result<Bar<'f>> {
    unsafe {
        let foo = foo as *mut Foo;

        match foo.as_mut().unwrap().method_a() {
            Ok(bar) => Ok(bar),
            Err(ref e) if e.kind() == ErrorKind::WouldBlock => foo.as_mut().unwrap().method_b(),
            Err(e) => Err(e),
        }
    }
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.