So I’ve been thinking about this a lot, because it’s something that I want in theory, to the point where I’ve already implemented over a half dozen float wrapper types in my own code. However, I’m skeptical that it will be ergonomic, and I’d like to outline what I think the basis of that is.
If we take a look at String, we can see it as a wrapper around Vec<u8> that maintains a UTF-8 invariant, but in practice that is not all that it is, and I’d argue that it is not enough to only do that. The difference is that String provides a complete suite of total (as in non-partial) methods for manipulating UTF-8 data. I’m imagining what I would be doing if it didn’t: I’d likely use Vec as an ‘ASCII’ type and convert to String only when I wanted/needed to verify it as UTF-8.
EDIT: Actually, the property I’m referring to above should be closure, not totality/partiality.
Anyway, the problem I’ve always had with my own float wrappers is that float operations are almost all partial, so you’ve got to exclude them (consider /=, for instance), or ‘escape’ your wrapper type, which hurts ergonomics by making you re-wrap. And in practice, I’ve gotten so frustrated with this that I only use them for Ord now. (There’s a similar level of frustration around as.) The trouble here being that this encourages you to only use these wrapper types at API boundaries, where an assertion would do just as well.
Maybe something could be done by introducing a whole zoo of wrapper types (Finite, NonZero, NonNan, NonNegative), but I feel like these will all have the same essential problem, and it’s not clear to me that we are doing anything but re-implementing float semantics at that point.
The ergonomic loss here is not in calling the div function, but in how you get your FiniteNumber and FiniteNonZeroNumber values. Calling the constructors for those types is no better than asserting that your denominator is nonzero. It’s potentially even worse. Sure, your div never panics, but your FiniteNumber constructor does… so we’re just pushing panics around unless you can statically determine all of your input values. And if you can do that, then just do CTFE and give a compiler error when a panic would occur.