Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-641 · Case file with fixtures · Case 613 of 694 · Compiler evidence

A Prefixed Integer Literal Still Needs at Least One Valid Digit

Base prefixes select how digits are interpreted; they are not values themselves. Validate generated numeric fragments, use separators for review, and keep bit masks typed to their domain.

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
A numeric literal template emitted its base selector even though its value fragment was empty.
First discriminating check
Restore at least one valid digit, then verify base, integer width, mask meaning, and the generator's empty-input policy.

0b, 0o, and 0x tell Rust which base to use for the digits that follow. A prefix without digits contains no number to interpret. The failing fixture writes 0b alone and receives E0768.

Fix token formation before type inference

This diagnostic happens while Rust is recognising the numeric token. A type annotation cannot supply missing digits, and changing from u8 to u64 cannot repair it.

The official E0768 explanation adds at least one digit. The Reference defines integer literals, their base prefixes, valid digits, suffixes, and underscores.

The repaired fixture uses a typed binary literal and asserts its decimal value. I include that assertion because it proves meaning rather than only syntax.

Base notation should clarify the domain

Binary reads well for small flags and register fields. Hexadecimal is compact for byte masks, addresses, and protocol constants. Decimal is often clearest for counts and timeouts. Octal remains common for Unix permission bits.

I choose notation from how engineers compare the value against a specification. 0b0000_0101 can reveal set bits more quickly than 5, while 500 milliseconds is clearer in decimal. Underscores group digits without changing value.

A suffix such as _u8 makes range and arithmetic behaviour visible. Unsuffixed literals use inference and defaults, which may be fine locally but can obscure shift and bitwise operations at interfaces.

Empty output often points to a generator bug

Humans rarely type a bare 0b intentionally. A template may produce it when a feature list is empty or a string conversion returns no digits. I inspect the data flowing into the literal rather than hard-coding one digit at the final output.

An empty set of flags might correctly be zero, spelled 0b0, but the generator must choose that policy. It should validate every digit against the base and range-check before producing Rust.

Token-aware code generation is safer than concatenating "0b" + bits. Tests include zero, one, maximum, separators, invalid digits, and widths crossing the destination type. The generated file is compiled on the pinned toolchain.

Parsing runtime data is a different operation

Rust source literals are not how I parse a binary string received at runtime. u32::from_str_radix(text, 2) returns a result that handles empty input, invalid digits, and overflow. The input normally excludes the 0b prefix unless preprocessing deliberately accepts it.

I keep source generation and runtime parsing separate. Turning untrusted text into Rust source and compiling it expands the security boundary far beyond parsing a number.

Errors should identify the field, base, and accepted range without echoing sensitive surrounding data. A configuration parser can reject 0b as incomplete or interpret an explicitly documented empty payload as zero; this is a product grammar decision.

Bit operations need range and shift review

A valid literal can still produce a wrong mask. Shifting by an out-of-range amount, applying a mask of the wrong width, or confusing bit index with bit value are common errors.

I name masks after capabilities, use constants of the exact storage type, and test every defined bit against examples from the protocol or hardware document. The Book’s operator appendix lists bitwise and shift operators, but the domain specification gives them meaning.

For flags exposed outside Rust, I preserve unknown bits when forward compatibility requires it or reject them when strict validation is safer. A literal test should cover that policy.

My E0768 checklist

  • Which base prefix has no valid digit after it?
  • Did a human omit the value, or did a generator receive an empty set?
  • Is zero the correct policy for empty flags, and is it written explicitly?
  • Does binary, octal, hexadecimal, or decimal best match the specification?
  • Is the integer width explicit and large enough for every defined bit?
  • Are underscores grouping meaningful fields rather than decorating randomly?
  • Is runtime text parsed with a fallible radix API instead of source generation?
  • Do tests prove masks, shifts, unknown-bit policy, and boundary values?

The core principle is that a base prefix changes representation of a value; it cannot replace the value. E0768 catches an incomplete token. I restore the digits, then verify width and bit meaning against the real domain contract.