ouuan
ouuan

Reputation: 511

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)
    }
}

Upvotes: 1

Views: 149

Answers (2)

caelansar
caelansar

Reputation: 323

According to rfc 3498

the opaque return type will automatically capture all trait input type and lifetime parameters, all type and lifetime parameters present in the Self type, and all type and lifetime parameters in the associated function or method signature.

the code can be desugar like this:

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

    fn baz() -> impl Foo {
        let a = A();

        Self::bar(&a)
    }
}

baz expects to return impl Foo while bar returns impl Foo + 'a, so the compiler complains borrowed value does not live long enough

The code in this answer works because the return type Self::BarRet of bar has no generic lifetime parameters, which means return value of bar has nothing to do with the lifetime of its parameter &A

Upvotes: 4

true equals false
true equals false

Reputation: 793

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

Upvotes: 3

Related Questions