Reputation: 41
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
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