"borrow later used here" I don't understand

fn main() {
    let a = String::from("foo");
    let b = create_b(&a);
    let _moved_a = a;
    b.test();
}

fn create_b(_unused: &str) -> B {
    B { b: "foobar" }
}

struct B<'a> {
    b: &'a str,
}

impl<'a> B<'a> {
    fn test(&self) -> Option<&str> {
        None
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0505]: cannot move out of `a` because it is borrowed
 --> src/main.rs:4:20
  |
3 |     let b = create_b(&a);
  |                      -- borrow of `a` occurs here
4 |     let _moved_a = a;
  |                    ^ move out of `a` occurs here
5 |     b.test();
  |     - borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0505`.
error: could not compile `playground`

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

I don't understand the error here.
Can anyone help me understand how "a" and "b" are related?
I found out about the Polonius borrow checker and tried that as well but it has the same error.

This is a stripped down example. It happened when using App from clap in our code.

1 Like

Rust uses fairly simple rules when filling in elided lifetimes that don’t look at the function body. Writing the lifetime names explicitly, create_b has the signature:

fn create_b<'a>(_unused: &'a str)->B<'a>;

Because 'a appears in both the argument and the return type, the compiler will require that _unused remain valid as long as the returned value exists.

In this example, you’re actually returning a B<'static> because it contains a reference to a string literal. If you declare that in the function signature, the code compiles:

fn create_b(_unused: &str) -> B<'static> {
    B { b: "foobar" }
}

(Playground)

1 Like

That was fast, thank you!

Understood. So this might actually work in the future automatically should the borrow checker learn to understand these kind of constructs.

It would be technically possible, yes, but it’s unlikely to happen. Function signatures in Rust are generally treated as compiler-enforced contracts that constrain how both the caller and function body must behave; this is the foundation that lets the semantic versioning scheme work for crates.

If your original function was in a public API, for example, you’re reserving the right to change the implementation in the future to one that borrows from its argument. Even though the current implementation doesn’t, the compiler will force callers to act like it does so that your future change won’t break their code.

Conversely, the new function signature promises that the function doesn’t borrow from its argument. That lets callers use it like your example did, but restricts what changes can be made to the function interior without breaking things.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.