Skip to content

Unnecessary lifetime error #109591

Description

@ViktorWb

The following code produces a compiler error:

pub trait ReturnedTrait {}

impl<T> ReturnedTrait for T {}

pub fn create_trait<'a, T>(_t: T) -> impl ReturnedTrait + 'a {}

pub fn outer<'a>(val: String) -> impl ReturnedTrait + 'a {
    create_trait(&val)
}
error[[E0597]](https://doc.rust-lang.org/stable/error_codes/E0597.html): `val` does not live long enough
 --> src/lib.rs:8:18
  |
7 | pub fn outer<'a>(val: String) -> impl ReturnedTrait + 'a {
  |              -- lifetime `'a` defined here
8 |     create_trait(&val)
  |     -------------^^^^-
  |     |            |
  |     |            borrowed value does not live long enough
  |     argument requires that `val` is borrowed for `'a`
9 | }
  | - `val` dropped here while still borrowed

playground

The reason, as I see it, is that according to RFC 1951, a returned impl captures generic arguments. However, in this case, I do have a lifetime bound 'a which is present on the returned impl but not on the generic T. However, the function still acts as if T has a lifetime bound of 'a.

Okay, so if the returned impl captures T, then I would expect to be able to use T in the return value. But the compiler will not let me:

pub trait ReturnedTrait {}

impl<T> ReturnedTrait for T {}

pub fn create_trait<'a, T>(t: T) -> impl ReturnedTrait + 'a {
    t
}

pub fn outer<'a>(val: String) -> impl ReturnedTrait + 'a {
    create_trait(val)
}
error[[E0309]](https://doc.rust-lang.org/stable/error_codes/E0309.html): the parameter type `T` may not live long enough
 --> src/lib.rs:6:5
  |
6 |     t
  |     ^ ...so that the type `T` will meet its required lifetime bounds
  |
help: consider adding an explicit lifetime bound...
  |
5 | pub fn create_trait<'a, T: 'a>(t: T) -> impl ReturnedTrait + 'a {
  |                          ++++

playground

The compiler will not let me use T in the returned impl, but still, it requires that T is borrowed for 'a.

I would expect one of two things to happen:

  • The returned impl captures T, which would allow me to use T in the return value.
  • The returned impl does not capture T, in which case it would be possible for T to not have lifetime 'a.

Instead what happens is that the inner function does not allow the impl to capture T, but the outer function acts as if the impl has captured T.

An additional note

I noticed that by wrapping the returned value in a Box<dyn _>, the code compiles:

pub trait ReturnedTrait {}

impl<T> ReturnedTrait for T {}

pub fn create_trait<'a, T>(_t: T) -> impl ReturnedTrait + 'a {}

pub fn outer<'a>(val: String) -> impl ReturnedTrait + 'a {
    let res = create_trait(&val);
    let res: Box<dyn ReturnedTrait + 'a> = Box::new(res);
    res
}
Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.76s

playground

This further leads me to believe that this lifetime error is unnecessary. Without any modification to the inner function, the compiler now understands that the returned impl in fact does not capture T.

Solution

Because of the 'a bound on the returned impl, the inner function does not allow the returned impl to capture T. The function outer should therefore be able to know that the parameter T does not have to live for 'a. Therefore, it should be possible to remove the lifetime error in the first code example, at this playground.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions