Amulepe Weichan
Amulepe Weichan

Reputation: 41

Lifetime on a single argument function

I've read the Rust Book and some other tutorials on lifetimes. I thought I was understanding lifetimes as like a generic parameter that a function takes, and that how you assign it to the input and output parameters of your function constrains the lifetime of those parameters. So like fn find<'a, 'b>(pattern: &'a str, text: &'b str) -> &'b str means anything passed in as text needs to have a lifetime at least as long as the return value. But on a program I'm writing I got an error that makes me think I'm misunderstanding something fundamental about lifetimes.

I have a function with signature:

async fn check_git(requester: &mut Requester) -> Result<()>

Result is anyhow::Result if it matters.

When I compile I get the error:

error[E0726]: implicit elided lifetime not allowed here
 --> src/scanner/git.rs:5:40
  |
5 | pub async fn check_git(requester: &mut Requester) -> Result<()> {
  |                                        ^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
  |
  = note: assuming a `'static` lifetime...

I fixed the compiler error by adding a lifetime parameter: async fn check_git<'a>(requester: &mut Requester<'a>) -> Result<()>. I understand requester can't have a static lifetime, as the way I'm calling check_git, the lifetime of the requester I pass in is the scope of the function calling check_git. But I don't understand exactly the meaning of the lifetime annotations I added.

My beginner understanding of lifetimes was that fn check_git<'a> is just specifying that 'a is a generic parameter, and then when you apply 'a to more than one parameter it constrains how the lifetimes of those parameters relate. But when I only apply 'a to one parameter there's no added constraint or meaning from the lifetime annotation?

The Requester struct I'm adding the lifetime parameter to is defined as:

struct Requester<'a> {
    client: SendRequest<Body>,
    resp: Option<Response<Body>>,
    host: &'a str,
    port: u16,
    ctx: &'a SslContextRef,
}

Upvotes: 4

Views: 725

Answers (1)

Kevin Reid
Kevin Reid

Reputation: 43892

But when I only apply 'a to one parameter there's no added constraint or meaning from the lifetime annotation?

Right. The rule here is not about "you should specify an additional constraint". Rather, it's that when you use a type that has a lifetime parameter, you should make it syntactically visible that there's a lifetime involved. For references, the & symbol itself is considered sufficient, but for named types, you're supposed to make the lifetime itself visible.

(It's still allowed to use hidden lifetime parameters in non-async functions, though I recommend prohibiting them with the lint configuration #![deny(rust_2018_idioms)].)

This doesn't mean you have to introduce an 'a though; you can specify '_, which means essentially “please use the lifetime elision rules” and thus, often but not always, “please use an anonymous distinct lifetime here”. So, write:

pub async fn check_git(requester: &mut Requester<'_>) -> Result<()> {

This follows the same rules as elided & lifetimes, so in this case it's the same as writing the elision-less form

pub async fn check_git<'a, 'b>(requester: &'a mut Requester<'b>) -> Result<()> {

Upvotes: 8

Related Questions