简体   繁体   中英

blanket trait implementation for generic trait

the rust docs gives the following example for blanket implementations

impl<T: Display> ToString for T {
    // --snip--
}

My somewhat more complicated trait does not allow me to write a blanket implementation.

/// a fixed size 2D array (WxH) that can be indexed by [usize;2]
pub trait Grid<C: Copy + Eq, const W: usize, const H: usize>:
    IndexMut<[usize; 2], Output = Option<C>>
{
}

// allow to transform one grid implementation into another 
// as long as they contain the same elements and have the same size
impl<C, O, I, const W: usize, const H: usize> From<I> for O
where
    O: Default + Grid<C, W, H>,
    I: Grid<C, W, H>,
    C: Copy
{
    fn from(input: O) -> O {
        let mut output = O::default();
        for i in 0..O::W {
            for j in 0..O::H {
                output[[i, j]] = input[[i, j]];
            }
        }
        output
    }
}

The errors say

the type parameter `C` is not constrained by the impl trait, self type, or predicates

the const parameter `W` is not constrained by the impl trait, self type, or predicates
expressions using a const parameter must map each value to a distinct output value
proving the result of expressions other than the parameter are unique is not supported

It feels like it is constrained via the O: Grid<Player, W, H> but that doesn't seem to be the right constraint.

Rhe errors around the const generic parameters are secondary (a red herring?). When I replace them with constants, the error around C (the element type) still remains.

The problem is that your variables C , W , and H could have more than one value, as far as the compiler is concerned. Consider what happens if I implements Grid<Foo, 1, 2> and also Grid<Foo, 500, 50> : it is ambiguous which Grid implementation your blanket From implementation should be using. ( C can't actually have multiple values for a given O or I , but the compiler doesn't actually reason that out.)

The solution to that problem is to change your trait's generics to associated types (and associated constants), which means that the trait cannot be implemented more than once for the same self type:

use std::ops::IndexMut;
/// a fixed size 2D array (WxH) that can be indexed by [usize;2]
pub trait Grid: IndexMut<[usize; 2], Output = Option<Self::Component>>
{
    type Component: Copy + Eq;
    const WIDTH: usize;
    const HEIGHT: usize;
}

// allow to transform one grid implementation into another 
// as long as they contain the same elements and have the same size
impl<O, I> From<I> for O
where
    O: Default + Grid,
    O::Component: Copy,
    I: Grid<Component = O::Component, WIDTH = O::WIDTH, HEIGHT = O::HEIGHT>,
{
    fn from(input: O) -> O {
        let mut output = O::default();
        for i in 0..O::W {
            for j in 0..O::H {
                output[[i, j]] = input[[i, j]];
            }
        }
        output
    }
}

However, this still will not compile, for several reasons:

  • You can't write a From impl for any two types that meet some trait bounds, because it might overlap with some other crate's From impl for any two types that meet different bounds — and it always overlaps with the blanket From<T> for T in the standard library.

    In general, you can only implement From<TypeYouDefined> for T or From<T> for TypeYouDefined — not a fully generic, blanket From impl. You can define your own conversion trait, though.

  • You can't actually write a bound that two traits' associated constants are equal, as my code suggests — the compiler assumes it must be a type constraint. I don't know if there's a solution to that problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM