4

I have a trait that defines an interface for objects that can hold a value. The trait has a way of getting the current value:

pub trait HasValue<T> {
    fn get_current_value(&self) -> &T;
}

This is fine, but I realized that depending on the actual implementation, sometimes it's convenient to return a reference if T is stored in a field, and sometimes it's convenient to return a clone of T if the backing field was being shared across threads (for example). I'm struggling to figure out how to represent this in the trait. I could have something like this:

pub enum BorrowedOrOwned<'a, T: 'a> {
    Borrowed(&'a T),
    Owned(T)
}

impl<'a, T: 'a> Deref for BorrowedOrOwned<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        use self::BorrowedOrOwned::*;

        match self {
            &Borrowed(b) => b,
            &Owned(ref o) => o,
        }
    }
}

And change get_current_value() to return a BorrowedOrOwned<T> but I'm not sure that this is idiomatic. BorrowedOrOwned<T> kind of reminds me of Cow<T> but since the point of Cow is to copy-on-write and I will be discarding any writes, that seems semantically wrong.

Is Cow<T> the correct way to abstract over a reference or an owned value? Is there a better way than BorrowedOrOwned<T>?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Wesley Wiser
  • 9,491
  • 4
  • 50
  • 69
  • Would returning an associated type `B: Borrow` work? – Chris Emerson Dec 14 '16 at 15:34
  • @ChrisEmerson I'm not sure what the tradeoffs of doing that would be so I'm unsure. Could you expand on that a bit? This seems like a question that could be generally useful so I'm interested in all answers even if they don't address my specific situation. – Wesley Wiser Dec 14 '16 at 15:36
  • I think actually it doesn't really help directly; you still end up needing something like `Cow`. – Chris Emerson Dec 14 '16 at 16:39

1 Answers1

9

I suggest you use a Cow, since your BorrowedOrOwned has no difference to Cow except that it has fewer convenience methods. Anybody that gets a hold of a BorrowedOrOwned object could match on it and get the owned value or a mutable reference to it. If you want to prevent the confusion of being able to get a mutable reference or the object itself, the solution below applies, too.

For your use case i'd simply stay with &T, since there's no reason to make the API more complex. If a user wants a usize, when T is usize, they can simply dereference the reference.

An owned object only makes sense, if you expect the user to actually process it in an owned fashion. And even then, Cow is meant to abstract over big/heavy objects that you pass by ownership for the purpose of not requiring anyone to clone it. Your use case is the opposite, you want to pass small objects by ownership to prevent users from needing to copy small objects, and instead you are copying it.

oli_obk
  • 28,729
  • 6
  • 82
  • 98
  • Thanks for the advice. In simplifying the problem, I misstated my reasons for sometimes wanting to return a `&T` and other times needing to return a `T`. If the implementation stores `T` in an `Arc>` (for example) then it isn't possible to simply return a `&T`. `T` must be cloned and since the clone lives on the function's stack, you can't return a reference to it. I hope that's helpful. PS. It was great talking to you at RBR :) – Wesley Wiser Dec 14 '16 at 16:16
  • 1
    Ah! That makes more sense. Well.. I then suggest that your `BorrowedOrOwned` should be `enum Borrow<'a, T> { Ref(&'a T), Arc(Arc>), Rc(Rc>) }` or something along those lines. It's complicated by the `RefCell`, since many use cases might not want a `RefCell` in there, but without knowing more about the use case, I'd guess that would solve it. – oli_obk Dec 15 '16 at 08:12
  • Very true. I'll have to play around with it a bit but I'm going to mark this as the answer since you've answered my question about `Cow` and this has definitely pointed me in the right direction. Thanks – Wesley Wiser Dec 15 '16 at 15:57
  • 1
    The term `Cow` is a bit confusing for new-comers, especially when dealing with read-only return values that you never intend to copy-from or write-to. While I was aware of `Cow` I was looking for something like `MaybeOwned`. But seems `Cow` is used for this too. – ideasman42 Jan 25 '17 at 19:55