Ergonomics of wrapping operations

aha, I see that now. Thanks for the clarification :smile: consider this a friendly bump for the topic then :slight_smile:

Yeah, the current recommendation is to either use wrapping_xxx methods or the Wrapping<T> types, whichever is more convenient.

I am curious to find out why all of your code would need to switch to Wrapping. I'd have thought that you'd only need to deal with Wrapping at the interface boundary?

FWIW, one of the major pain-points with Wrapping<T> is that heterogeneous operations - e.g. T + Wrapping<T> - are not permitted. This is because itā€™s not obvious whether such an operation should wrap or not. In different words, one of the operands essentially needs to be casted before performing the operation, and itā€™s not clear which one.

Iā€™m now kind of leaning towards saying that Wrapping<T> should ā€œwinā€: in other words, that we should have impl Add<u32> for Wrapping<u32> { type Output = Wrapping<u32>; ... } (and vice versa), and likewise for the other types. The intuition is this: the reason why ā€œplainā€ T (e.g. u32) panics on over/underflow is not because this is well-known to be the behavior thatā€™s desired by users of u32; rather, the reason is precisely that we donā€™t have any information about the programmerā€™s intent, so our only reasonable option is to signal the unhandled circumstance by panicking. (This is also underscored by the fact that panicking only happens in debug builds: if we knew that panicking is what the programmer explicitly wants, then we would do it always.)

Now in the case of u32 + Wrapping<u32>, if we think of it as a unification problem, then ā€œno available informationā€ unifies with ā€œit should wrapā€ to yield ā€œit should wrapā€.

(Incidentally, for completenessā€™s sake, we should presumably also have Saturating<T>, Checked<T>, and Overflowing<T> for the other possible policies, with analogous impls. And see also.)

I disagree. This would amount to making one Wrapping value ā€œinfectā€ all surrounding operations with non-wrapping values; and sometimes wrapping can lead to counter-intuitive results. Therefore if we allow it, the Output type should be u32. One can always add another Wrapping(ā€¦) around it to ensure the result wraps.

Hmm. The idea was actually to protect against accidents by giving a type error if u32 was expected but a Wrapping<u32> resulted (i.e., the scenario where I wrote T + Wrapping<T> but didnā€™t actually want it to wrap). Iā€™m not sure how that squares with the ā€œunification of informationā€ idea, actually. I can see how it might backfire when the result type is inferred instead of checked.

One place where there arenā€™t questions around the choice of result type, though, is AddAssign (+=) and family. I think it would be completely, unambiguously fine to have impl AddAssign<u32> for Wrapping<u32> as a wrapping operation. (Right now it seems we donā€™t even have AddAssign<Wrapping<u32>> for Wrapping<u32>!)

I worry that this could get rather confusing for more complicated expressions. For example, even just u32 + u32 + Wrapping<u32> would yield a Wrapping<u32>, but could still panic if the first addition overflowed. Maybe thatā€™s what is desired, but it seems more likely that it isnā€™t.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.