Understanding bounds on trait definition

Hi folks,

I'm trying to better understand the meaning of bounds on the definition of a trait. Concretely, I'm fighting this in a context where I want my trait to have IndexMut<...> as a supertrait (which has Index<...> as a supertrait), but also require that the associated type Index<...>::Output satisfies From<f64> so that I can assign f64 values via the mutable index.

My confusion is highlighted in this playground, reproduced below.

use std::ops::{Index,IndexMut};

// I want something like this to mean "MatrixTrait can only be implemented if
// IndexMut is also implemented SATISFYING THE TRAIT BOUND", but it does not
// appear to be so, otherwise line (*) below would already be implied.
trait MatrixTrait: IndexMut<(usize,usize)>
where
    <Self as Index<(usize,usize)>>::Output : From<f64>
{
}

struct Matrix {
    data: [[f64; 3]; 3]
}

impl Index<(usize,usize)> for Matrix {
    type Output = f64;
    fn index(&self, (i,j): (usize,usize)) -> &Self::Output {
        &self.data[i][j]
    }
}
impl IndexMut<(usize,usize)> for Matrix {
    fn index_mut(&mut self, (i,j): (usize,usize)) -> &mut Self::Output {
        &mut self.data[i][j]
    }
}

impl MatrixTrait for Matrix {
}

fn act_on_matrix<T>(U: &mut T) -> ()
where
    T : MatrixTrait,
    // will not compile if (*) is commented out
    // <T as Index<(usize,usize)>>::Output : From<f64> // (*)
{
    U[(1,1)] = 0.0.into();
}

fn main() -> () {
    let mut U = Matrix{data: [[0_f64; 3]; 3]};
    act_on_matrix(&mut U);
}

In this example, I would have expected the bound on the definition of MatrixTrait to imply that any generic type satisfying this trait would also satisfy the bounds. However, in the code above it seems necessary to explicitly write out the trait bound a second time in the generic function, otherwise the following error is given:

error[E0277]: the trait bound `<T as Index<(usize, usize)>>::Output: From<f64>` is not satisfied
  --> src/main.rs:33:9
   |
6  | trait MatrixTrait: IndexMut<(usize,usize)>
   |       ----------- required by a bound in this
7  | where
8  |     <Self as Index<(usize,usize)>>::Output : From<f64>
   |                                              --------- required by this bound in `MatrixTrait`
...
33 |     T : MatrixTrait,
   |         ^^^^^^^^^^^ the trait `From<f64>` is not implemented for `<T as Index<(usize, usize)>>::Output`
   |
help: consider further restricting the associated type
   |
33 |     T : MatrixTrait, <T as Index<(usize, usize)>>::Output: From<f64>
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Since my interpretation clearly seems to be off, how should one read bounds on trait definitions? Is there a way to achieve the goal of bounding implementors of the trait without having to repeat the bound in all users of types satisfying the trait? I.e. can this bound somehow live as part of the supertrait declaration, so that it is implied by T : MatrixTrait?

Thanks!

I think this is a case of:
https://github.com/rust-lang/rust/issues/20671

1 Like

Thanks. That indeed seems to be the right lead! Guess this is something the compiler could in principle support but simply does not right now... too bad.

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.