Representing a data structure that has optional components

let rev: ReversibleMove = "Rd2xBd6".parse();
let alg: LongMove = rev.into();
assert_eq!(alg, "Rd2xd6");

This is a conversion that can be done without a board, as it just removes the target piece.
If it were represented with a network of Into/From and IntoWithBoard, then this would just work.

Because Ideally I would want that to be checkable at compile-time, which seems to be only possible using a lot of types.

I think I would rather use a type for each of the notations than that, but thanks for the example!

You can also write this code with a single move type:

let move1: Move = "Rd2xBd6".parse().unwrap();
assert_eq!(move1.long_algebraic_notation(), "Rd2xd6");
assert_eq!(move1.short_algebraic_notation(&position), "Rdxd6");
let move2 = Move::from_short_algebraic_notation("Rdxd6", &position).unwrap();
assert_eq!(move1, move2);

Then what data would be stored in the Move struct? Would it include the target piece? In that case, Move::from_short_algebraic_notation could never exist, as it doesn't have enough information to fill it. Would it not include that target piece? Then Move::long_algebraic_notation could never exist, as the parse call would throw away that data, or the parse call on the first line would throw an error, not expecting that extra information.

It could go either way -- I'd say don't include the target piece, just the moving piece and from and to squares.

It does have all the information from the position -- notice the &position argument in my example code.

Note that the code examples we are giving are not really realistically useful code. In realistic code you are not parsing a hardcoded string, you're parsing moves from a database or something. In that context, it is not very useful to parse short notation like "N4xe6" (or worse, "cd") without seeing the position for context.

Long algebraic notation doesn't contain the captured piece.

Reversible notation includes the captured piece, so if you don't store it, then you have to write it as move1.reversible_notation(&position). I think that's OK. Reversible notation is not commonly used anyway.

2 Likes

The typestate builder pattern doesn't preclude the use of individual types, it can really just act as an intermediary which gives the compile time guarantees you're after. Also, because empty fields are zero sized types there is no memory overhead for it.

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.