rusttraitslifetimeborrow-checker

Rust thinks argument is borrowed in the `impl Trait` return value and complains "borrowed value does not live long enough"


Simplified code:

struct A(/**/);

trait Foo {}

trait Bar {
    fn bar(a: &A) -> impl Foo;

    fn baz() -> impl Foo {
        let a = A();
        Self::bar(&a)
    }
}

Error:

error[E0597]: `a` does not live long enough
  --> src/lib.rs:10:19
   |
9  |         let a = A();
   |             - binding `a` declared here
10 |         Self::bar(&a)
   |         ----------^^-
   |         |         |
   |         |         borrowed value does not live long enough
   |         argument requires that `a` is borrowed for `'static`
11 |     }
   |     - `a` dropped here while still borrowed

It can be resolved by returning a concrete type that implements Foo or Box<dyn Foo> instead of impl Foo, but I want to keep returning impl Foo without introducing additional overhead.

Examples that work but I don't want:

struct A(/**/);

trait Foo {}

struct B;
impl Foo for B {}

trait Bar {
    fn bar(a: &A) -> B;

    fn baz() -> impl Foo {
        let a = A();
        Self::bar(&a)
    }
}
struct A(/**/);

trait Foo {}

trait Bar {
    fn bar(a: &A) -> Box<dyn Foo>;

    fn baz() -> Box<dyn Foo> {
        let a = A();
        Self::bar(&a)
    }
}

Solution

  • I don't fully understand why it doesn't work, but a workaround is to make the return type an associated type to the trait:

    struct A(/**/);
    
    trait Foo {}
    
    trait Bar {
        type BarRet: Foo;
        fn bar(a: &A) -> Self::BarRet;
    
        fn baz() -> impl Foo {
            let a = A();
            Self::bar(&a)
        }
    }
    

    playground