How does one implement `ToOwned` for a wrapper of a reference?

I have a type that looks like this:

#[derive(Debug, Copy, Clone)]
pub enum Block<'a> {
    // ...
}

It implements both Copy and Clone because all it holds inside is references.

What I now want to add is this:

#[derive(Debug, Clone)]
pub enum OwnedBlock {
    // ...
}

impl<'a> ToOwned for Block<'a> {
    type Owned = OwnedBlock;

    fn to_owned(&self) -> Self::Owned {
        OwnedBlock::from_block(*self)
    }
}

Block here is like a str and OwnedBlock is like String.

But turns out alloc already provides an impl that conflicts with this:

error[E0119]: conflicting implementations of trait `alloc::borrow::ToOwned` for type `block::Block<'_>`
   --> src/block.rs:472:1
    |
472 | impl<'a> ToOwned for Block<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: conflicting implementation in crate `alloc`:
            - impl<T> alloc::borrow::ToOwned for T
              where T: core::clone::Clone;

I understand why the default impl might seem to be useful, but it isn't for me here.

I'm okay with usage of nightly-only features.

There's no direct way around the blanket implementation, but even if there was, you presumably can't meet the associated type requirement either:

impl<'a> Borrow<Block<'a>> for OwnedBlock {
    // The signature approximately requires you to own the `Block<'_>`,
    // e.g. in a field.
    fn borrow(&self) -> &Block<'a> { ... }
}

You can perhaps work around both with enough indirection...

...though I don't know if it's worth it.

impl From<Block<'_>> for OwnedBlock is an alternative.

1 Like

Yeah, does standard library have a special case for built-in types? If that is the case, then there might be a chance to get the same functionality for custom types at some point.

There's no special casing going on for std here. The uncovered blanket implementation is for Sized T, and all the other std implementations are for unsized types like str. There's no overlap because Sized is a fundamental trait -- you implement it from day one or it is a breaking change to implement it -- so the coherence checker can assume str, etc, will never implementat Sized.

(Clone requires Sized to boot, but Clone itself is not fundamental.)

If you had a struct and not an enum, a custom unsized type might have been another way forward (although they have their own drawbacks too).

The dyn workaround is itself a custom unsized type in some sense, I suppose.

1 Like

I see, Sized is the key piece here, which str doesn't implement

1 Like