简体   繁体   中英

Traits with stricter associated type bounds than supertrait

I have a simple trait with an associated type with no bounds.

trait Board {
    type Move;
    fn moves(&self) -> Vec<Self::Move>;
}

I also want to use this trait as a supertrait. In particular, I want my new subtrait to have stricter bounds on the associated type. Something like this:

trait TextBoard: Board {
    type Move: fmt::Debug; // Trying to tighten bounds on associated type
    fn printMoves(&self) {
        println!("{:?}", self.moves());
    }
}

The example is highly simplified, but seems to show the problem: The compiler thinks I'm trying to create a new associated type, but I just want the subtrait to require tighter bounds. Is there any way to achieve this?

Here's what you asked for:

trait TextBoard: Board
where
    Self::Move: Debug,
{
    // ...
}

All the bounds on a trait have to be in the "headline"; you can't impose additional restrictions once you start writing the body of the trait. This bound will prevent you from writing impl TextBoard for Foo when <Foo as Board>::Move does not implement Debug ( playground ).


Maybe that is what you want, but do you really need to prevent implementing TextBoard for other types? For some type there could be another way to write print_moves that makes more sense, and the Debug requirement is just noise. In that case you probably want to skip the where clause and move the body of print_moves to a blanket impl :

trait TextBoard {
    fn print_moves(&self);
}

impl<B: Board> TextBoard for B
where
    B::Move: Debug, // or <Self as Board>::Move: Debug
{
    fn print_moves(&self) {
        println!("{:?}", self.moves());
    }
}

With this version, you still don't need to write an impl for types where Self::Move: Debug , but you're not prevented from writing an impl for other types where that doesn't hold. It's more of an extension than a refinement .


On the other hand, you should pretty much always implement Debug for every type, so is it really useful to have that trait? Maybe what you want is just an optional method on Board that's implemented when Move: Debug :

trait Board {
    type Move;

    fn moves(&self) -> Vec<Self::Move>;

    fn print_moves(&self)
    where
        Self::Move: Debug,
    {
        println!("{:?}", self.moves());
    }
}

This is like the original version, but doesn't require the addition of a new TextBoard trait, so it will probably cut down on the number of explicit bounds you have to write. Many of the standard library traits such as Iterator have optional methods defined with bounds like this. The downside, besides the requirement that Move must be Debug , is that it clutters the Board trait with printing code, which you might not consider really part of what it means to be a Board .

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