Trait fn implies borrow of self when I don't want it to

I need help with a mysterious lifetime issue. The error that the compiler is reporting is as follows:

   Compiling my-crate v0.6.0 (...path...)
error[E0515]: cannot return value referencing local variable `named_actor`
  --> (path)/assertion.rs:90:17
   |
88 |       let summary = named_actor.to_summary();
   |                     ----------- `named_actor` is borrowed here
89 |
90 | /     IdentityAssertionReport {
91 | |         signer_payload: SignerPayloadReport::from_signer_payload(&self.signer_payload),
92 | |         named_actor: Some(summary),
93 | |     }
   | |_________________^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.

The definition of the trait containing to_summary fn is here:

pub trait ToCredentialSummary {
    fn to_summary(&self) -> impl Serialize + 'static;
}

I'm not aware of anything that ties the output of to_summary to any ongoing lifetime of the type that implements ToCredentialSummary , but the compiler seems to think there is one.

Can you help me understand this implied lifetime or – even better – explain how to write to_summary to that the return type can outlive the implementation of ToCredentialSummary?

(edited to remove indentation that made it harder to read …)

You want to clone any data in named_actor referenced in to_summary so the reference is not alive when you hand named_actor to IdentityAssertionReport.

-> impl in traits -- and everywhere in edition 2024+ -- captures all borrows from the inputs. You can read more about it here. So you have to train your eyes to pick up on impl in a return the same way they pick up on & and '_.

In a trait, you need a nightly feature for the ideal fix, unfortunately.

#![feature(precise_capturing_in_traits)]
pub trait ToCredentialSummary {
    fn to_summary(&self) -> impl Serialize + use<Self>;
}

(Tracking issue.)

Another alternative is to use an associated type instead.

pub trait ToCredentialSummary {
    type Summary: Serialize;
    fn to_summary(&self) -> Self::Summary;
}

But that doesn't help if your type is unnameable unless you can type erase it (Box<dyn ..>). And it makes for a lot more boilerplate on your side if you want it to be opaque like -> impl is.

There's another feature that will help with that eventually.

#![feature(impl_trait_in_assoc_type)]
impl ToCredentialSummary for Something {
    type Summary: impl Serialize;
    fn to_summary(&self) -> Self::Summary { .. }
}

(Tracking issue, for type aliases generally, not just in traits. Edit: Also various discussions in this PR.)

1 Like

@quinedot been doing some reading since then and this looks like an accurate description of the problem space. I'm not able to use nightly features(*), so it looks like I'll be Boxing the result instead. Thanks for the confirmation.

(*) This code will be going into a public crate and I can't impose a requirement on our users to build with nightly Rust.