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
.