From review of #461 (r3616036865). Confirmed still present at the end-of-train tip. Target: cow-venue (L3). Not a defect; a question about whether the machinery earns its keep.
OrderBuilder<S> in cow-venue/src/order.rs is a typestate builder: a PhantomData<S> state parameter, four state markers (NeedsBuy, NeedsSell, NeedsValidTo, Ready), per-state impl blocks, and build available only on OrderBuilder<Ready>.
The question
OrderBody's required fields (sell and buy side plus amount, valid_to) are flat and mutually independent. Nothing in CoW's wire format constrains the order in which they are set, only that all of them end up set. Typestate earns its cost when call order is semantically constrained, for example must-sign-before-submit. Here the invariant is really "do not forget a required field", which is a weaker claim than the machinery expresses.
Options
- Keep it. The compile-time completeness guarantee is real, and for a financial order type a missing required field is worth catching at compile time rather than runtime. If kept, say so in the type's rustdoc so the next reader does not re-open this.
- Required args in the constructor, optional setters after.
OrderBuilder::new(sell, buy, valid_to) followed by optional setters keeps the same compile-time completeness guarantee with no phantom states, no per-state impl blocks, and no duplicated method surface. This is the strongest alternative: it does not trade safety for simplicity, it gets both.
Result-returning build. Simplest surface, but moves a caught-at-compile-time error to runtime. Weakest option for this domain; listed for completeness only.
Acceptance criteria
Either the typestate is replaced by required-args-in-constructor with the completeness guarantee preserved, or the typestate stays and its rustdoc states why the state machine is worth the surface.
OrderBuilder<S>incow-venue/src/order.rsis a typestate builder: aPhantomData<S>state parameter, four state markers (NeedsBuy,NeedsSell,NeedsValidTo,Ready), per-state impl blocks, andbuildavailable only onOrderBuilder<Ready>.The question
OrderBody's required fields (sell and buy side plus amount,valid_to) are flat and mutually independent. Nothing in CoW's wire format constrains the order in which they are set, only that all of them end up set. Typestate earns its cost when call order is semantically constrained, for example must-sign-before-submit. Here the invariant is really "do not forget a required field", which is a weaker claim than the machinery expresses.Options
OrderBuilder::new(sell, buy, valid_to)followed by optional setters keeps the same compile-time completeness guarantee with no phantom states, no per-state impl blocks, and no duplicated method surface. This is the strongest alternative: it does not trade safety for simplicity, it gets both.Result-returningbuild. Simplest surface, but moves a caught-at-compile-time error to runtime. Weakest option for this domain; listed for completeness only.Acceptance criteria
Either the typestate is replaced by required-args-in-constructor with the completeness guarantee preserved, or the typestate stays and its rustdoc states why the state machine is worth the surface.