Passing a reference to an API


#1

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


#2

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


#3

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).


#4

You could take config by reference instead of by value.

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


#5

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


#6

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.


#7

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.