Assignment requires that [var] is borrowed for [lifetime]

I'm running into an interesting problem that can't quite wrap my head around. I'm using the openssl crate and I'm attempting to assign one of the structs there to my own struct

pub struct HmacCtx<'a> {
    signer: Signer<'a>,
}

impl <'a> HmacCtx<'a> {
    pub fn new(digest: MessageDigest, key: &[u8]) -> Result<Self, HmacError> {
        let pkey = PKey::hmac(key)?;
        Ok(Self {
            signer: Signer::new(digest.into(), &pkey)?
        })
    }
}

The code above gives me an error saying:

24 | impl <'a> HmacCtx<'a> {
   |       -- lifetime `'a` defined here
...
28 |             signer: Signer::new(digest.into(), &pkey)?
   |                     ---------------------------^^^^^--
   |                     |                          |
   |                     |                          borrowed value does not live long enough
   |                     assignment requires that `pkey` is borrowed for `'a`
29 |         })
30 |     }
   |     - `pkey` dropped here while still borrowed

I can't seem to see how it is that I make something borrowed for a specific lifetime, I thought I did that by declaring the lifetime in the struct definition. Any help would-be be much appreciated

Keep in mind that lifetime declarations don't do anything. They're stripped out completely before the code is compiled. The lifetime declarations are only assertions/tests for what the code is actually doing.

And what your code is actually doing, is creating pkey, then destroying it, and then trying to use it in a struct. All variables are destroyed as soon as they go out of scope, so you can't use &pkey in a struct that is returned out of scope of pkey variable. This variable won't exist any more when the struct is returned, so it's not a problem with lifetime declarations. It's an actual use-after-free bug that Rust is preventing.

Is Signer a struct you've defined? If so, don't use references (temporary borrows) in structs. This creates this type of problem — the Signer struct refuses to store the data (references never store anything), so you have no place to store the PKey object you've created.

If it's not your struct, then you will have to accept an existing pkey as an argument (so that the caller finds a place where to store it). Other options are ugly hacks: leak memory (Box::leak), or create self-referential struct by putting PKey in a Box in the HmacCtx, and then override pkey's lifetime with unsafe code to be able to pass it to Signer:

struct HmacCtx {
   signer: Signer<'static>,
   pkey: Box<PKey>, // order of fields is important for destruction
}
2 Likes

Signer is a struct that's defined in the openssl crate, so I don't have control over it. I will have to figure out some other way to deal with it