Mehdi Akiki
Rust Failure Atlas / Upgrades and compatibility

RFA-323 · Case file with fixtures · Case 295 of 694 · Runtime evidence

next_multiple_of Panics When the Divisor Is Zero

next_multiple_of must produce an integer multiple and therefore panics for a zero divisor. checked_next_multiple_of turns zero and representability overflow into None, leaving the caller to distinguish or reject invalid configuration.

Reviewed
Rust
Rust 1.98.1, edition 2024
Targets
all Rust targets
Profiles
dev, release, test

Direct answer

What this Rust failure means

Why it happens
next_multiple_of must produce a representable multiple and defines zero as an invalid divisor, unlike a nearby total Boolean predicate.
First discriminating check
Validate zero separately and exercise checked_next_multiple_of for both the zero-divisor and representability-overflow None results.

I had an alignment helper that rounded a byte count to the next block size. A disabled configuration passed block size zero, and next_multiple_of panicked in a place that looked like harmless arithmetic.

The failing program catches the operation and then shows that the call did not succeed.

Producing a multiple needs a nonzero divisor

u32::next_multiple_of returns the smallest multiple of the right-hand operand greater than or equal to the receiver. Its documentation states that it panics when the divisor is zero.

There is no ordinary sequence of positive steps of size zero from which to select a rounded-up result. More importantly for systems code, zero alignment normally indicates invalid configuration rather than a useful rounding request.

The panic is not restricted to debug builds. A zero divisor violates the operation's contract independently of overflow-check settings.

The checked method covers two failure causes

checked_next_multiple_of returns None instead of panicking. It returns None both for a zero divisor and when the next multiple cannot be represented in the integer type.

The repaired program demonstrates all three branches: zero produces None, seven rounded to a multiple of four produces eight, and rounding u32::MAX to a multiple of two produces None because the mathematical answer is outside u32.

That merged None is often sufficient for a low-level helper. At a configuration boundary I usually want better diagnostics, so I reject zero first and then map a remaining None to an overflow error.

A nearby predicate has a different zero rule

This can be surprising after reading is_multiple_of. That predicate gives a defined answer for zero. In particular, zero is a multiple of zero under its documented total relation, while nonzero values are not.

There is no contradiction. A predicate can classify every pair without constructing a new value. next_multiple_of promises to return an actual integer in the type. Its result-producing contract has representability and divisor preconditions that the Boolean relation does not need.

I avoid learning a “zero rule” for the whole numeric type. I learn the rule for each operation.

Alignment types remove a repeated precondition

If every call requires a nonzero alignment, I parse configuration into NonZeroU32 or a domain-specific Alignment type. The constructor can also enforce a power of two when the allocator, file format, or hardware interface requires it.

Then the rounding function receives a value whose invalid zero state cannot appear. I still handle result overflow because a valid alignment plus a very large input can exceed the output type.

This is stronger than writing if alignment == 0 at several call sites. Validation happens once, and signatures show the assumption.

Do not replace the method with the familiar formula casually

The common expression (value + alignment - 1) / alignment * alignment has more failure edges than it appears to have. It can underflow at alignment - 1 for zero, divide by zero, overflow the addition, or overflow the final multiplication.

Changing the operation order may avoid one overflow while introducing another. Bit-mask rounding only applies under specific power-of-two and representation assumptions.

The standard checked method gives a compact, documented operation. A wrapper can convert its Option into the error vocabulary of the application.

I test configuration and arithmetic separately

For an alignment boundary, my cases include:

  • zero alignment;
  • alignment one;
  • a value already aligned;
  • a value one below and one above a boundary;
  • the largest valid input;
  • a valid divisor whose next result overflows;
  • any domain rule such as power-of-two-only alignment.

I also check the type conversion before rounding. A signed configuration parsed and cast to unsigned can turn -1 into a very large number, creating a different failure that the rounding helper cannot explain.

Panic or Result is an API decision

For a private function called only after validated construction, a panic on impossible zero may be acceptable and expose an internal invariant violation. For user input, file headers, network values, and changing runtime configuration, returning a structured error is normally better.

The error should identify whether the divisor was zero or the result overflowed. Operators can fix “block size must be nonzero” more easily than “rounding failed.”

The core principle is that predicates and constructors have different totality requirements. is_multiple_of can define a Boolean result at zero, while next_multiple_of cannot manufacture the promised representable multiple. I validate the divisor and keep overflow explicit instead of letting configuration become a distant arithmetic panic.