API That let's the user decide who owns data

I'm trying to understand how I would create an API where the user decides whether to pass ownership to the struct, or to pass a reference to the struct. What I came up with is this:

use std::borrow::Cow;

struct Complicated<'a> {
    maybe_owned: Cow<'a, str>
}

impl<'a> Complicated<'a> {
    fn new(value: Cow<'a, str>) -> Self {
        Self{
            maybe_owned: value
        }
    }

    fn value(&self) -> &'a str {
        self.maybe_owned.as_ref()
    }
}

fn main() {
    let static_str = "static";
    let owned_str = "owned".to_owned();
    let complicated_static = Complicated::new(Cow::Borrowed(static_str));
    let complicated_owned = Complicated::new(Cow::Owned(owned_str));
    println!("{}", complicated_static.value());
    println!("{}", complicated_owned.value());
}

It doesn't compile with:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:20:26
   |
20 |         self.maybe_owned.as_ref()
   |                          ^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 19:5...
  --> src/main.rs:19:5
   |
19 | /     fn value(&self) -> &'a str {
20 | |         self.maybe_owned.as_ref()
21 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:20:9
   |
20 |         self.maybe_owned.as_ref()
   |         ^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 12:6...
  --> src/main.rs:12:6
   |
12 | impl<'a> Complicated<'a> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:20:9
   |
20 |         self.maybe_owned.as_ref()
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^

In my mind 'a represents the lifetime of the struct, and any data borrowed by the struct, so it seems like returning &'a str would indicate that all is well, so long as the returned ref does not outlive the struct, which does not outlive the thing passed in with new.

I'm either way off, or close. Not sure which. Is this type of interface possible, where the user decides whether to move data into the struct, or to retain ownership (say for performance)?

What would a minimal implementation look like, if so?

So in this case, 'a doesn't represent the lifetime of the struct. It represents the lifetime of the borrowed variant within the Cow. Since it only applies to the Borrowed variant, and not the Owned, it also means that 'a doesn't really apply to the Owned variant.

You can either solve this by giving &self its own lifetime and using that same lifetime in the return type, or let the elision rules take over and leave out all lifetimes on fn value. The reason you have to do this is because of the Owned case where the borrow's lifetime stems from the object itself and not from the borrowed case.

giving &self its own lifetime and using that same lifetime in the return type

Ah yes, that worked.

For the next step, extra-credit, I'm trying to figure out if there is a signature for new where both of these would just 'work'.

new("x") and new(String::from("x")).

I know how to make such a signature work with new<S: AsRef<str>, but I don't know how to interop that with Cow.

Yes, take a generic parameter of Into<Cow<..>> see https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b15082adccb9ddb0835cd4085e1663b2

Nice! Thank you!

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.