Wrapping either an owned value or reference

Hi there,

I am writing a struct that will wrap a value that cannot be cloneable (e.g. a database connection). I looked for similar examples of what I am trying to achieve but in all cases the wrapped value is cloneable and the Cow smart pointer is used. I couldn't find any smarter pointer that would satisfy my use case, so I am using something along the lines of the following code:

enum Either<'a, T> { Owned(T), Borrowed(&'a T) }

#[derive(Debug)]
pub struct NonCloneableValue;

pub struct Wrapper<'a>(Either<'a, NonCloneableValue>);

impl <'a> Wrapper<'a> {
    pub fn from_borrowed(v: &'a NonCloneableValue) -> Wrapper<'a> {
        Wrapper(Either::Borrowed(v))
    }

    pub fn from_owned(v: NonCloneableValue) -> Wrapper<'static> {
        Wrapper(Either::Owned(v))
    }

    /// Gets a reference to the value.
    pub fn get_value(&self) -> &NonCloneableValue {
        match &self.0 {
            Either::Owned(v) => &v,
            Either::Borrowed(v) => v,
        }
    }
}

The code is available at the Rust Playground.

I would like to ask if there is a more idiomatic way in Rust for the code I wrote above, perhaps some existing std struct or library I am not aware of.

Many thanks for your time!

Why do you think the Cow doesn't satisfy your use case? In the playground example you're clearly reinventing the Cow type.

1 Like

Cow requires ToOwned, which has a blanket implementation for Clone types but is not usually applicable to non-Clone types.

1 Like

You could simplify this slightly by using just an enum, rather than an enum inside of a struct: Rust Playground

If you do this, you can still make a "specialized" version using a type alias:

type FooWrapper<'a> = Wrapper<'a, Foo>;

I would also implement the Deref trait for Wrapper, for more convenient access to its contents.

1 Like

Aha, here's a published crate with a type like the one you've written:

3 Likes

If you don’t need both owned and borrowed values to have exactly the same type, you can make your wrapper hold a generic Ptr:Borrow<NonCloneableValue>. This will work for owned values, references, and most smart pointers.

Thanks @mbrubeck , that's exactly what I was after!

@rlcintra

I'm curious regarding what it was in your codebase that was requiring that your type be cloneable in the first place?.. i.e., the code that was trying to clone something (e.g., the db connection) you did not want to clone.

Cow requires the type to implement ToOwned, which is pretty much an extension of Clone (in fact ToOwned is implemented for any type that implements Clone).

1 Like

So the Cow feature for a db connection provides features that make the wrapper work-around worth it? Where I'm going with this is what is triggering the need to copy the db connection?

It doesn't need to copy the db connection, but just to use the type Cow<'a, DbConnection> somewhere DbConnection need to implement ToOwned. The definition itself just uses the ToOwned::Owned associated type, but by requiring ToOwned to be implemented it also requires the method ToOwned::to_owned to be implemented which in turn requires making a clone of the implementer (in this case DbConnection). However ToOwned::to_owned is used only in Cow::to_mut, Cow::into_owned and Cow::clone, so it's a bad API that it's also required for any other method.

1 Like

Yes. My understanding was that what triggers the need to copy is a need to write (copy on write). If the copy feature is disabled using the work-around, then how accomplish managing single write access? At that point, what benefit does Cow still provide?

It still allows you to store either a reference o an owned value.

Does The Borrow trait not fo that?

That's not a type you can store

Perfect. What do you mean by “store”?

For example as a field of a struct, or inside a Vec

1 Like

Thank you.