Smart pointer which owns its target

Ah, now I understand that part. Yes, that makes sense.

:roll_eyes:

Okay, but at what price?

I think both your approach as well as my approach has downsides. Perhaps it's ultimately a matter of taste?

Exactly my point.

Using a trait is sometimes painful because it requires the user of a crate to bring all necessary traits in scope. See also: Pub use Trait as _ for more hygiene.

That's why I said earlier:

With non-sealed traits (like GenericCow), however, it can be a hygiene problem, as they might be implemented by other crates.

On the other hand:

  1. It's possible that mmtkvdb re-exports deref_owned::GenericCow, thus GenericCow can be seen as part of mmtkvdb. Of course, then this argument still strikes:
  1. But regarding "straightforwardness", I feel like
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Owned<T>(pub T);

impl<B> Borrow<B> for Owned<<B as ToOwned>::Owned>
where
    B: ?Sized + ToOwned,
{
    fn borrow(&self) -> &B {
        self.0.borrow()
    }
}

is less trickery than what was needed in your playground here:

(Even if this can be encapsulated/hidden from the user. But a maintainer of mmtkvdb or a fork of it would have to overlook it.)

My mind has already been almost exploding several times. Understanding this issue was several days of work (but I learned a lot during that process, admittingly).

I think I was violating everything that this video would have taught me if I had seen it earlier (quote from another thread):

I feel like adding yet another (non-generic) wrapper structure makes things even worse for me.

  1. In practice, the lack of MethodReceiver<T> might not be so bad. Consider this:
struct Fancy;

impl Fancy {
    fn fancy(&self) {
        println!("YAY!")
    }
}

struct SomeType;

trait Abstract {
    type Retval; // no bounds at all
    fn foo(&self) -> Self::Retval;
}

impl Abstract for SomeType {
    type Retval = Fancy;
    fn foo(&self) -> Self::Retval {
        Fancy
    }
}

fn main() {
    let v = SomeType;
    // we can call `.fancy()`, even if `Abstract::Retval` has no bounds
    v.foo().fancy();
}

(Playground)


(Preliminary) Conclusion

I would like to give my own (preliminary) conclusion from this discussion:

  • My original approach (e.g. deref_owned version 0.2.0) was flawed because it "abused" Deref (see IntoOwned in version 0.2.0). (Many thanks to you for helping me figuring that out and being so patient with me.)
  • This has been solved by using Borrow instead (see GenericCow in version 0.8.0).
  • Ergonomics of GenericCow are somewhat limited. In the generic case, deref-coercion won't work and we need the weird Owned wrapper when providing an always-owned GenericCow value.
  • However, ergonomics aren't totally bad:
    • In the concrete case, deref-coercion still works (see also Playground above) because for example Owned still implements Deref (without abusing it for wrong reasons and without impeding transitivity of dereferencing).
    • Writing Owned(x) instead of Cow::Owned(x) isn't really that hard. In fact it's shorter. :wink:
  • In the future, the Owned wrapper might or might not become superfluous.
  • Sometimes, providing concrete types instead of impl Trait may be beneficial.
  • Most people will just use Cow and not go crazy like I almost did. :face_with_spiral_eyes: