Newtype wrapper conflicting From<T> for T

I'm getting conflicting a implementation of impl<T> From<T> for T {} when I try to implement:

trait Inner: From<Self::I> {
    type I;
    fn inner(self) -> Self::I;
    fn wrap(value: Self::I) -> Self { value.into() }
}

struct OuterWrapper<V: Inner>(pub V);

impl<V: Inner> From<<V as Inner>::I> for OuterWrapper<V> {
    fn from(value: <V as Inner>::I) -> Self { OuterWrapper(value.into()) }
}

[playground]

Newtypes are supposed to avoid exactly this issue. Can anyone help sort out how <V as Inner>::I could possibly be appearing as OuterWrapper<V>??

The problem is that there is nothing stopping someone from implementing Inner for some V with type I = OuterWrapper<V>. What you have here is a slightly unusual pattern. The more usual approach (which does work) is to implement From for something which implements Inner directly:

impl<V: Inner> From<V> for OuterWrapper<V> {
    fn from(value: V) -> Self { OuterWrapper(value.into()) }
}

What is I intended to represent here?

2 Likes

Ahh, that makes sense... And, of course, there's no !Self trait bound to block that possibility.

I just want to be able to chain Inner upward through wrappings. I represents the core wrapped value:

struct InnerA(pub f64);

impl From<f64> for InnerA {
    fn from(value: f64) -> Self { InnerA(value) }
}

impl Inner for InnerA {
    type I = f64;
    fn inner(self) -> Self::I { self.0 }
}

struct InnerB(pub f32);

impl From<f32> for InnerB {
    fn from(value: f32) -> Self { InnerB(value) }
}

impl Inner for InnerB {
    type I = f32;
    fn inner(self) -> Self::I { self.0 }
}

Basically, Wrapper<V: SomeMarker> is great, but I need to embed a concrete type at the center. So, doing math using two concrete types will give a fixed concrete type back, I just want to be able to have it .into() the function's named return type as outermost wrapper. So I don't have to name the full stack: Descriptive<Wrapper<Owned<TensorF64>>>.

The failing case would be a recursive wrapper holding an infinite number of itself with no inner value, which quite definitely IS impossible to implement. Can that be expressed so the compiler can understand?

You can use the transitive_from crate for implementing From like that.

If the paths that you try to implement this for are one-directional, then you can actually solve it without macros, but I'm not sure it's worth the hassle.

1 Like

Well, if your concern is just being able to do arithmetic being generic over the inner wrapper, then there's no need for a complicated From implementation, you can just make the arithmetic operators generic over Inner. For example:

impl<V: Inner> Add for OuterWrapper<V>
where
    <V as Inner>::I: Add<<V as Inner>::I, Output = <V as Inner>::I>,
{
    type Output = OuterWrapper<V>;

    fn add(self, right: OuterWrapper<V>) -> OuterWrapper<V> {
        V::wrap(self.0.inner() + right.0.inner()).into()
    }
}

(playground)

This approach is a bit constrained in that it doesn't allow differences in the output type.

I'm not quite sure what you're trying to rule out here. A type with direct recursion usually gets rejected by the compiler because it is infinite in size. Recursion can be achieved with boxing of the inner value, but that wouldn't apply to wrapper types where the wrapped type is a generic parameter (as is the case with Wrapper in your code), since the instantiation of the generics would end up infinitely long. You would only end up with a recursive type inside the wrapper if you actually put a recursive type inside the wrapper, and then you can never construct it to be infinite length anyway.

Exactly that. If the inner type held by the opaque newtype V within a Wrapper<V> is another Wrapper<V>, then the V held by that will hold another Wrapper<V>, on and on forever. The compiler just doesn't know via a generic that V is V(pub I) with I being named as Inner::I.
Thus, impl<V: Inner<I = Wrapper<V>>> From<<V as Inner>::I> for Wrapper<V> is technically for an impossible type, but it still evaluates as being a possibly-nameable target for From<T> for T

That's exactly what I'm trying to generalize over. I'm looking for the most ergonomic way to work this, (where the output could be an I or an I2 depending on broadcasting):

impl<I: Inner, I2: Inner> Add<Wrapper<I2>> for Wrapper<I>
where
    <I as Inner>::I: Add<<I2 as Inner>::I>,
    I: OpWith<I2>, // Helper trait naming the expected output
    // And here is where it doesn't come together
    <I as OpWith<I2>>::Output: From<<<I as Inner>::I as Add<<I2 as Inner>::I>::Output>,
{
    type Output = Wrapper<<I as OpWith<I2>>::Output>;

    fn add(self, rhs: Wrapper<I2>) -> Self::Output {
        Wrapper((self.inner() + rhs.inner()).into())
    }
}

Yes, just one-direction and verifiable at compile time. I want to name a set of types placed in opaque wrappers implementing Inner where I is each one's held type. Then I name owned, reference, and mutable wrappers noted as holding same-rank/equivalent types for ops and assign ops. And at last wrap the owned/ref/mut types in the interface type via Marker of Wrapper<V: Marker + Inner>(pub V) and want to get the From for free.

And I think that's the answer. Leave From out of Inner and manually implement lifting the wrappings.

This is just a comment about the approach you abandoned; feel free to ignore it. (I don't believe there's a blanket implementation that will work, and that the transitive crate you marked as the solution is your best bet going forward.)


Here's a non-infinite failing case.

struct S1;
impl Inner for S1 {
    type I = OuterWrapper<S1>;
    fn inner(self) -> Self::I {
        OuterWrapper(self)
    }
}

Now when V = S1, your blanket implementation is

impl From<OuterWrapper<S1>> for OuterWrapper<S1>

Nothing infinite going on, but definitely a conflict.

Yeah, I understand what could be set up to break it. It's my own usage and setup that I can guarantee, and often there are neat trait bound tricks to encode things like that. This is a sealed trait inside an internal crate. But it seems it would take a negative trait bound to guarantee Self::I is the only contained value in a set of wrappers or that I'm not uselessly short-circuiting it.

The transitive crate is actually a step too far. I just want to go one direction with a deterministic setup. The real solution was that it's not worth the bother of trying to use From, giving me this workaround with implementations for layers above the first then having to manually pass the wrap to the inner Inner:

struct WrapsInner<T: Inner>(pub T);

impl<T: Inner> Inner for WrapsInner<T> {
    type I = <T as Inner>::I;

    fn inner(self) -> Self::I { self.0.inner() }

    fn wrap(value: Self::I) -> Self {
        // All I lose is the default implementation by `value.into()` here
        WrapsInner(
            T::wrap(value)
        )
    }
}

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.