at54321
at54321

Reputation: 11866

Why "expected named lifetime parameter" in a generic function

If I write a function like this:

fn parse_json<'a, T: Deserialize<'a>>(s: &'a str) -> Vec<T> {
    serde_json::from_str(s).unwrap()
}

it works as expected. But if I try to do the same w/o explicitly declaring lifetimes, like this:

fn parse_json<T: Deserialize>(s: &str) -> Vec<T> {
    serde_json::from_str(s).unwrap()
}

I get a compiler error:

| fn parse_json<T: Deserialize>(s: &str) -> Vec<T> {
|                  ^^^^^^^^^^^ expected named lifetime parameter

Why? Is there a reason for the compiler to require explicit lifetime declaration in this case?

If I hadn't used a generic but a concrete type, I wouldn't need to explicitly declare any lifetimes:

fn parse_json(s: &str) -> Vec<MyStruct>   // that compiles file

That, I believe, is due to the so called lifetime elision rules. Question is, why doesn't the version with a generic return type get covered by the elision rules as well?

Upvotes: 4

Views: 11388

Answers (1)

Camelid
Camelid

Reputation: 1595

Lifetime elision applies to function arguments and return types, but it does not currently apply to trait bounds, such as T: Deserialize.

Why? Is there a reason for the compiler to require explicit lifetime declaration in this case?

I don't know for sure, but in general, the lifetime elision rules are fairly restricted, presumably to make them easier to understand and reduce the chances of introducing confusing compiler "magic".


Note that lifetime elision is supported in generic parameters, but it is semi-deprecated (see the elided_lifetimes_in_paths lint):

struct Foo<'a>(&'a str);

// Compiles, but causes a warning with `#![warn(rust_2018_idioms)]`
fn bar1(s: &str) -> Foo { Foo(s) }

// Compiles, does not cause warning
fn bar2(s: &str) -> Foo<'_> { Foo(s) }

Upvotes: 2

Related Questions