rust-lang / git2-rs

libgit2 bindings for Rust
https://docs.rs/git2
Apache License 2.0
1.67k stars 384 forks source link

Wrong elided lifetimes for Commit and Signature #895

Closed bcmyers closed 1 year ago

bcmyers commented 1 year ago

This issue is very similar to #299, which was resolved in this PR: https://github.com/rust-lang/git2-rs/pull/302

In addition to filing this issue, I'm also submitting a new PR to address it.

Motivating example

This code should compile

struct Wrapper<'repo> {
    inner: git2::Commit<'repo>,
}

impl<'repo> Wrapper<'repo> {
    fn email(&self) -> Option<&'repo str> {
        let signature = self.inner.author();
        let email = signature.email();
        email
    }
}

but fails with

error: lifetime may not live long enough
 --> src/lib.rs:9:9
  |
5 | impl<'repo> Wrapper<'repo> {
  |      ----- lifetime `'repo` defined here
6 |     fn email(&self) -> Option<&'repo str> {
  |              - let's call the lifetime of this reference `'1`
...
9 |         email
  |         ^^^^^ associated function was supposed to return data with lifetime `'repo` but it is returning data with lifetime `'1`

error[E0515]: cannot return value referencing local variable `signature`
 --> src/lib.rs:9:9
  |
8 |         let email = signature.email();
  |                     ----------------- `signature` is borrowed here
9 |         email
  |         ^^^^^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.

Underlying issue

On the Commit struct, there are several "getter" methods where the lifetime of the return object is tied to a borrow of Self:

In other words, written without elision, these signatures all look like

But the pointers that are returned by these methods all live as long as the 'repo lifetime of the Commit struct itself if you look at the C code. In other words, they should all have these (more flexible) signatures:

struct Commit<'repo> { ... }

impl<'repo> Commit<'repo> {
    pub fn author(&self) -> Signature<'repo> { .. } 
    pub fn body(&self) -> Option<&'repo str> { .. }
    // Etc.
}

There is a very similar problem happening for the "getter" methods of the Signature struct.

The C code

The equivalent "getter" methods are defined in the underlying C code is here:

The pointers they return live here:

I believe all these pointers live as long as this raw *mut raw::git_commit pointer here:

which has the 'repo lifetime.