Lifetime problem: how to return a struct with a reference to a variable created within the function

Hi all! I'm new to Rust, and I'm trying to learn as quickly as possible but some of the concepts are difficult for me.

I was trying to refactor some code I was working on to pull some common logic into a function. And that led me to write something like this:

// cargo-deps: jsonwebtoken="*", openssl="*", derive_more="*"
use jsonwebtoken::DecodingKey;
use openssl;
use openssl::pkey::PKey;
use openssl::error::ErrorStack;

fn main() {
    pub fn decoding_key_for_rsa_public_key(
        public_key: &PKey<openssl::pkey::Public>,
    ) -> Result<DecodingKey, ErrorStack> {
        let public_key_der = public_key.rsa()?.public_key_to_der_pkcs1()?;
        let key = DecodingKey::from_rsa_der(&public_key_der);
        Ok(key)
    }
}

Note: jsonwebtoken isn't on Rust Playground or I would have posted it there. I made the above code usable in rust-script instead.

Now, I understand the problem here. public_key_der is a Vec<u8>, which only lives as long as the function block. key is going to try to hold a reference to public_key_der, but it will live on beyond the function block--meaning that it will be holding a reference to data that is no longer around, which is of course a bad idea.

(Note: the actual implementation of from_rsa_der creates a Cow::Borrowed of public_key_der, which makes me think that this is important for me, but I don't exactly understand what Cow does or why one uses it)

Anyway, I understand why the code as written doesn't compile. What I haven't figured out is, how to actually make it work. Truth be told, what I want is for public_key_der--the Vec[u8] created in this function--to last as long as the Ok(key) returned by this function does. After all, the only point of this vector is to be referenced by the DecodingKey object. I'd like to try to mark it as having the same lifetime as the object returned, and I'd really like to do so without marking things as having a static lifetime; but I can't seem to figure out how to do that.

I found a way around writing this method as it is, so answering this is not entirely necessary. But I would like to figure out if and how to do it this way. Can anyone help me out? Thanks!

Short answer to your title - you cannot.
The longer answer is that no matter what you do, if the variable is not stored somewhere else, which remains valid after the function returns, you will have a dangling reference. This is true even in C or C++. But unlike those languages, Rust simply won't allow you to compile such code.

2 Likes

You can try to return Ok(key.into_static()).

2 Likes

Right! That makes sense. So, I guess my question is--is there a way to store it somewhere such that it remains valid after the function returns?

Would I have to ask for a mutable reference to be input into the function, that I could write the Vec[u8] into? Or is there a simpler way?

Yes, I saw that. But if I understand the static lifetime correctly, it makes the data valid for the entire lifetime of the program?

That may not be a problem in my specific case, but I'm wary of it as a general solution...

With this particular library, it really means "turn this borrowed key into an owned key". Under the hood it is using Cows, which encapsulate "this thing is borrowed for 'some_lifetime or is owned." A Cow<'static, _> is either borrowed forever, or is owned; either way you can hold on to it for as long as you like. But if you don't hold on to the owned version forever, it doesn't keep the data around forever.

into_static is converting their Cow<'any, _> to Cow<'static, _> by converting the borrowed cases into owned cases.

TL;DR: In this particular case, into_static is akin to turning a &str into a String.

3 Likes

I see. So in this case, it sounds like into_static() does exactly what I want.

I had a feeling Cows was doing more than I understood, but I'm still not confident in it. I guess I need to learn and read more.

Thanks for your help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.