Passing a reference to an API

How can I fix the code below? I tried using owning_ref but I can’t get it to work.

extern crate owning_ref; // 0.4.0

struct ExternalAPIStruct<'a> {
    inner: &'a str,
}

impl<'a> ExternalAPIStruct<'a> {
    pub fn new(config: &'a str) -> Self {
        ExternalAPIStruct { inner: config }
    }
}

// can be changed
struct MyStruct<'a> {
    ext: ExternalAPIStruct<'a>,
}

impl<'a> MyStruct<'a> {
    pub fn new(config: String) -> Self {
        // can be changed
        MyStruct {
            // can't reference a local variable
            ext: ExternalAPIStruct::new(&config)
        }
    }
}

fn main() {
    let dynamic_str = "Hello there".to_owned();
    
    let _my_struct = MyStruct::new(dynamic_str);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing function parameter `config`
  --> src/main.rs:21:9
   |
21 | /         MyStruct {
22 | |             ext: ExternalAPIStruct::new(&config)
   | |                                         ------- `config` is borrowed here
23 | |         }
   | |_________^ returns a value referencing data owned by the current function

error: aborting due to previous error

For more information about this error, try `rustc --explain E0515`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

UPD:

Worked around with an owning_ref::OwningHandle (unfortunately, it uses unsafe):

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d595367093ffe38efcc222cd9faaa33c

You can do it like this, although you didn’t exactly state your needs, so it might be off.

References are for temporary values borrowed from somewhere else, and imply that you will not free that value.

I don’t see why would you put a temporary borrow in ExternalAPIStruct. If you’re going to store the config, it has to be String or Box<str> (note that both String and Box store data by reference. You don’t need Rust’s borrows to store or pass things by reference).

You could take config by reference instead of by value.

I.e. &'a str instead of String in new

That’s the catch. It’s an external API and the only constructor accepts &'a str where 'a must outlive the created object.

Uh, that API could have used Cow :confused:

In that case your new taking &'a str instead of String makes most sense.

Alternatively, if you don’t create many of these, Box::leak(String.into_boxed_slice()) can give you arbitrary 'static str at the cost of never freeing that memory.

Sure it’s gonna take some time to fill up 32GiB of RAM with Box::leak :slight_smile:

If you’re interested, here’s how I worked around the problem: https://github.com/greshake/i3status-rust/pull/317/files#diff-d25ef168d08ddcfeaca8453639097a59R205

Gonna think now how to avoid being unsafe.