Why is type parameter T unconstrained in this generic impl

I have the following minimal example:

use std::ops::{Deref, DerefMut};

trait Representation {
    fn as_void_ptr(&self) -> *mut std::ffi::c_void {
        self as *const Self as *mut _
    }
}

impl Representation for f32 {}

pub trait PtrMut<T> {
    fn ptr_mut(&mut self) -> &mut T;
}

struct MySlice<T> {
    a: Vec<T>,
}

impl PtrMut<f32> for MySlice<f32> {
    fn ptr_mut(&mut self) -> &mut f32 {
        &mut self.a[0]
    }
}

struct MyAsyncSlice<T> {
    a: MySlice<T>,
    ready: bool,
}

impl<T> Deref for MyAsyncSlice<T> {
    type Target = MySlice<T>;

    fn deref(&self) -> &Self::Target {
        &self.a
    }
}

impl<T> DerefMut for MyAsyncSlice<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.a
    }
}

trait DoSomething {
    fn do_something(&mut self);
}

When I write the following implementation, everything works as expected:

impl<D> DoSomething for D
where
    D: DerefMut<Target: PtrMut<f32>>,
{
    fn do_something(&mut self) {
        todo!()
    }
}

However, when I try to generalize it:

impl<T, D> DoSomething for D
where
    D: DerefMut<Target: PtrMut<T>>,
    T: Representation,
{
    unsafe fn do_something(&mut self) {
        todo!()
    }
}

The compiler complains with the following error: error[E0207]: the type parameter T is not constrained by the impl trait, self type, or predicates
I don’t understand why this happens. There can be only one implementation of DerefMut for a given type, and it has a single associated Target type. I am restricting DoSomething to be implemented only for types whose Target implements a specific generic trait, which I further constrain with T: Representation. It seems that every generic parameter should be perfectly determined by these predicates, yet the compiler disagrees.

I think the following is related to this issue as well, when I do:

impl<D> DoSomething for D
where
    D: DerefMut<Target: PtrMut<f32>>,
{
    fn do_something(&mut self) {
        todo!()
    }
}

impl<D> DoSomething for D
where
    D: DerefMut<Target: PtrMut<f64>>,
{
    fn do_something(&mut self) {
        todo!()
    }
}

compiler complains that:

error[E0119]: conflicting implementations of trait `DoSomething`
  --> src/test.rs:70:1
   |
61 | / impl<D> DoSomething for D
62 | | where
63 | |     D: DerefMut<Target: PtrMut<f32>>,
   | |_____________________________________- first implementation here
...
70 | / impl<D> DoSomething for D
71 | | where
72 | |     D: DerefMut<Target: PtrMut<f64>>,
   | |_____________________________________^ conflicting implementation

I don't understand why those are conflicting implementation. These types can't overlap since one can't have multiple implementation of DerefMut trait for the same type.

Can someone explain what's going on here?
Rust playground link

That's right this far, but your argument hasn't reached T yet. The Target type is constrained.. but that Target type then could have multiple distinct PtrMut implementions. If this isn't ever supposed to happen in practice, i. e. Foo: PtrMut<Bar> and also Foo: PtrMut<Baz>, then you can consider refactoring the PtrMut trait, turning the type parameter into an associated type.

3 Likes

To clarify further, I think of the type parameter is a type "input" into the trait (trait could be implemented for various parameters) whereas the associated types is a type output (only one associated type for a given trait impl)

3 Likes

Thank you. I now see an issue.
But what to do if I don't have control over PtrMut trait implementation, can I do anything about this without changing PtrMut code?

You could make T a parameter of DoSomething.

Or you could define your own helper trait different from PtrMut[1] to establish the type relationship between D or D::Target and T, and only support types that implement that one, too.


  1. which you could also have method-less and use besides PtrMut and/or give it a PtrMut-based supertrait bound ↩︎

1 Like