RFA-500 · Case file with fixtures · Case 472 of 694 · Compiler evidence
Numbers Cannot Be Cast Directly to Bool in Rust
Rust has no implicit or as-based truthiness conversion. Compare against the domain's valid values, and validate protocol encodings when integers mean more than zero versus non-zero.
- 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 cast changes representation while a boolean conversion chooses a domain predicate, and Rust requires that truth rule to remain explicit.
- First discriminating check
- Identify whether the input is a count, exact protocol flag, status, or bit field, then compare or validate precisely instead of assuming non-zero truthiness.
I tried to write 1_u8 as bool. Rust emitted E0054 because as does not define a conversion from an integer to bool.
The failing fixture may look surprising when coming from C-like systems where zero is false and non-zero is true. Rust requires me to state that rule as an expression rather than hiding it inside a cast.
Rust has booleans, not general truthy values
A condition such as if value expects value to have type bool. Integers, pointers, strings, and collections do not become conditions through implicit truthiness.
This keeps intent visible. count != 0, !items.is_empty(), and option.is_some() describe three different domain questions. Treating them all as truthy would discard those differences at the point where a decision is made.
The official E0054 page recommends an explicit comparison for numeric input.
The repaired fixture states one chosen rule
The repaired fixture uses raw != 0. This implements the conventional non-zero rule and produces a real boolean.
That rule is not universal. A protocol may define only 0 and 1, reserving every other byte as invalid. In that case raw != 0 wrongly accepts 2 and 255. I use a match or TryFrom conversion that returns an error for unknown encodings.
The compiler can require explicitness, but I still choose the correct domain semantics.
A cast and a comparison answer different questions
The Reference on type casts lists permitted cast categories. Numeric casts change representation or numeric domain under defined rules. A comparison evaluates a relation and yields bool.
Writing raw as bool would ask a representation operation to decide a predicate. Rust keeps those concepts separate. I find this especially valuable around FFI, binary formats, databases, and configuration sources where invalid values must not be silently normalised.
Explicit conversion also gives one place to document compatibility behaviour.
Converting bool in the other direction is different
Rust permits some casts from bool to integer types, producing zero or one. That does not imply a reversible integer-to-bool cast because many integers would map to only two boolean values.
Round trips matter. If a wire format requires u8, I can encode enabled as u8 deliberately. When decoding, I validate the admitted byte set rather than assuming the reverse cast exists.
This asymmetry prevents information loss from being mistaken for an identity conversion.
Boundary types make repeated validation unnecessary
If raw flags arrive throughout a codebase, I avoid scattering != 0 everywhere. I parse once into bool or a domain enum at the boundary. Internal functions then accept the validated type.
An enum may be better when states include Enabled, Disabled, Inherited, or Unknown. Compressing these into a boolean too early loses useful information and can produce unsafe defaults.
I let the external representation stay external.
Search terms can hide the real issue
Questions often ask “how to cast int to bool in Rust,” but the more useful question is “what should each integer value mean?” The mechanical answer is a comparison. The engineering answer is a decoding policy.
For a count, non-zero may be perfect. For a status code, one exact value may indicate success. For bit flags, a mask such as raw & ENABLED != 0 identifies one bit without treating unrelated bits as truth.
Tests should include the values around the boundary
I test zero, the documented true value, and at least one invalid or unrelated non-zero value. This prevents a broad != 0 rule from surviving when the protocol accepts only zero and one. For bit masks, I include another bit without the target bit and a value with both bits set. These examples are small, but they prove the conversion describes the real encoding. Property tests can help when the input covers every byte: every admitted representation maps predictably and every rejected representation stays rejected.
My E0054 checklist
- Is the source a count, protocol flag, status code, or bit field?
- Does every non-zero value really mean true?
- Should unknown values produce an error?
- Would an enum preserve more domain information?
- Can conversion happen once at the input boundary?
- Is a bit mask required instead of a zero comparison?
- Are encoding and decoding rules tested together?
- Does internal code now receive an actual
bool?
The core principle is that truth is a domain decision, not a numeric cast. Rust makes me write that decision explicitly, which is a useful chance to validate data instead of silently accepting every representation.