Hello, im trying to make poker evaluation library.
trait DeckSizeRules {}
struct Deck52 {}
impl DeckSizeRules for Deck52 {}
struct Deck36 {}
impl DeckSizeRules for Deck36 {}
trait CardGameRules {
type DeckSize: DeckSizeRules;
// more rules
}
struct Holdem {}
impl CardGameRules for Holdem {
type DeckSize = Deck52;
}
struct ShortDeckHoldem {}
impl CardGameRules for ShortDeckHoldem {
type DeckSize = Deck36;
}
// more games added by library users impl CardGameRules
Ok, I want to to implement common Eval trait based on CardGameRules and having difficulties. My question is what is right way to split Eval implementation for Deck52 and Deck36
I've tried
trait Eval1 {
fn eval() -> u32;
}
impl<T> Eval1 for T
where
T: CardGameRules<DeckSize = Deck52>,
{
fn eval() -> u32 {
52
}
}
impl<T> Eval1 for T
where
T: CardGameRules<DeckSize = Deck36>,
^^^^^^^^^^^ conflicting implementations
{
fn eval() -> u32 {
36
}
}
And
trait Eval2 {
type DeckSizeEval: DeckSizeEval<Self>;
fn eval() -> u32 {
Self::DeckSizeEval::eval()
}
}
impl<T> Eval2 for T
where
T: CardGameRules,
{
type DeckSizeEval = T::DeckSize;
^^^^^^^^^^^ the trait `DeckSizeEval<T>` is not implemented for `<T as CardGameRules>::DeckSize`
}
trait DeckSizeEval<T: ?Sized> {
fn eval() -> u32;
}
impl<T> DeckSizeEval<Deck52> for T
where
T: DeckSizeRules,
{
fn eval() -> u32 {
52
}
}
impl<T> DeckSizeEval<Deck36> for T
where
T: DeckSizeRules,
{
fn eval() -> u32 {
36
}
}
Why don't you just follow the compiler's suggestion? Your approach #2 can be trivially made compile by adding the single missing bound T::DeckSize: DeckSizeEval<T>, which you specified in the trait definition, but for some reason left out from the impl (why?). Playground.
Btw, if I add the desired fn main() to the above playground, it becomes apparent that among all these levels of indirection, you confused the role of the type parameter of the trait DeckSizeEval. In one place, it looks like it should be the game type, in another, it seems you want it to represent the deck type.
Adding explicit impls for DeckSizeEval<Holdem> etc. makes the code compile and pass the assertions, but that's probably not what you wanted, given that you likely need generic blanket impls so that user-defined games get those impls, too.
I have a feeling that you simply have too many traits and that some of them should simply be removed; reasoning across multiple levels of blanket impls is hard, and you should probably be using associated types instead.
Well, i'd like to see how i can use associated types in that case. There are many game types and idea is that library user can add something like IrishPoker, add CardRules and Eval for IrishPoker is ready.
Also, in reality eval is heavy, it uses huge tables for hand type recognition. So, if we use only Holdem and Omaha in final product, we don't want ShortDeck eval code linked.
Well OK. Then what do you need from the Deck associated type and trait? If you don't need them, then you can (and should, probably) just remove the bound.
Again, you are confusing the roles of the type parameter of DeckSizeEval. You are writing impls like DeckSizeEval<Deck36> but the thing that is actually supposed to impl Eval (which is apparently the game object itself) requires that T::DeckSize: DeckSizeEval<T>, i.e., the type parameter is now the game object, no the deck type. This doesn't compile for exactly the same reason as the previous playground I linked to.
You are saying that both the deck size and the particular cards are important (which I do get), but none of your examples so far actually use both of them at the same time. This doesn't help at all to guess what you are trying to do and how the two pieces of information are supposed to work together.
If you merely want to delegate evaluation from the game object to the deck, then you can do this.
The goal im trying to achieve: Eval implementation for different CardGameRules. DeckSizeEval it's just my attempt to split implementations for different deck sizes. I just started learning Rust and I'm sure there is more clever way to make it work.
Again, if you are trying to forward to the Deck then you can check my link above. If not, please elaborate as to what exact, specific function the Deck has, and precisely what pieces of information you need from it in order to implement CardGameRules::eval().
Yes, idea with 'delegation' is fine. But there are many other rules, not just DeckSize. I.e. CardGameRules::eval() want to know DeckType, IsHiGame, 'IsLoGame, 'IsHiLoGame, IsTripsBeatStraight and so on... and choose implementation of eval at compile time. I thought if i can split implementations for DeckSize then i can split for all.