Returning a String/str from an i32

I'm a Rust newbie here, I have an Option that I need to convert to an Option<&str> and not matter what I've tried I get:

error[E0515]: cannot return value referencing temporary value
   --> main.rs:104:13
    |
104 |             convert(&Some(s))
    |             ^^^^^^^^^-------^
    |             |        |
    |             |        temporary value created here
    |             returns a value referencing data owned by the current function

Here's my latest attempt based upon posts here

pub fn convertInteger(input: &Option<i32>) -> Option<&str> {
    match input {
        None => None,
        //
        Some(i) => {
            let s = i.to_string();
            Some(s.as_str())
        }
    }
}

A reference needs to borrow from something, ie. an owned value. If the value it borrows from lives inside the function, then you can't return a reference into it (since the owned value would be gone by the time the function returned).

Thus, a function with a signature like (&u32) -> &str is a code smell and likely has no useful implementation.

If you want to convert an integer to a string, then return String.

I appreciate the response and I agree that the function is likely overkill.

What I'm attempting to do is a classic data mapping from one struct to another and the source Struct has Option and the target type has Option<&str> thus I wanted to confirm that the value is present before the conversion.

I also tried it inline with the source object's field and that gave me the same error:

target_field: Some(input.field.unwrap_or_default().to_string().as_str())

Here you have a problem, as H2CO3 said, you are using a reference to a value that will get destroyed at the end of the function.

I think that the second structure don't match your use case. Could you show me your structs and write a few lines on your use case please :innocent: (But as he said, you probably really want String and not &str)

The problem is not the function. The problem is the lifetime mismatch – what you want is not possible, with a function or without. Inlining what the function does directly into the field doesn't work, as you observed, because then you have a temporary (as opposed to a local variable), which is destroyed at the end of the containing expression (as opposed to the end of the function). Whether it's the end of the function or the end of the expression doesn't matter – it's destroyed prematurely either way, so you simply can't take a reference to it. Period.

What you could do is declare a local variable that outlives the second, &str-containing struct. Then borrow that local variable in the &str-containing struct. This, of course, only works if you use and forget that struct right away, i.e. its lifetime is no longer than the String variable itself. I.e.:

let the_string = input.field.unwrap_or_default().to_string();
let the_struct = MyStruct { target_field: Some(the_string.as_str()) };
// use `the_struct`
// ...
// then it has to go out of scope before `the_string` does

Good call out, I'm using the OpenAPI generator to create the source data and on the back end, storing the data using Diesel and I'll readily admit it's me following the Diesel examples for how an insert struct should be created.

The fields in the data model are already defined as I'm trying to re-write some Java code in Rust so I'm attempting to not re-create it.

Here's a subset of the structs in question:

Source:

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct Sheet {
    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    #[serde(rename = "level", skip_serializing_if = "Option::is_none")]
    pub level: Option<i32>,

Target:

#[derive(Insertable, Debug)]
#[table_name = "my_table"]
pub struct MyTable<'a> {
    pub id: &'a str,
    pub capability_level: Option<&'a str>,

The other thing worth noting is that I'm iterating a Vec to performing the mapping and that looks like:

let cap : Vec<MyTable> = map
        .iter()
        .map(|(id, value)| {
          MyTable { 
              id: sheet.id.as_ref().unwrap().borrow(),
              capability_level:  Some(the_string.as_str()),
            }
}

Ok i see, so you won't be able to store the level as a &str because &str is a reference to an existing String, but here you don't have an existing String and you want to create it. but for the id it will probably work, because you will only referenced the existing one.

I say probably, because borrowing the old id means that you won't be able to manipulate the old struct as you want, and it means that after the conversion, you will have to keep the old table aswell.

If your goal is to make a conversion and so, you don't want to keep the old table, you should probably use into_iter which take the ownership of your data and then output a structure as follow :

pub struct MyTable {
    pub id: String,
    pub capability_level: Option<String>,
}
1 Like

That worked! Thank you! Now to make sure the insert into the DB works :slight_smile:

1 Like

Your problem was coming from a misunderstanding of how references work / are used in Rust, compared to higher level languages like java. I would recommend you to usually use owned data if you don't really get what's going on in memory. The problem with owned data, is that you need to clone it. And this is where having reference can help doing optimized stuff. But takes your time, it is not because you can optimize everything that you should do it. At least for now :wink:

1 Like

That for sure is the case as its been a long time since I've used a non-managed language (aka C/C++) so definitely learning the proper way to do things in Rust! I appreciate the help and the tips!