Rust's Standard Library Fails JSON Validation

Building software without third-party dependencies forces a deep dive into what a language's standard library truly offers. For one Rust developer, this exploration uncovered a significant flaw: Rust's built-in number parser accepts approximately 10% of JSON documents that RFC 8259 explicitly mandates should be rejected. This issue surfaced during a 72-hour hackathon challenge focused on creating a JSON toolkit in Rust with absolutely no external crates. The constraint meant relying solely on Rust's `std` library, leaving `Cargo.toml` empty save for the project itself.

The premise of the Zero Dependency hackathon is to test the limits and promises of standard libraries. The developer, identified as Pal11103 on Dev.to, found that while the goal was to build a functional JSON parser, the most striking revelation was not the difficulty of parsing, but the unexpected leniency of Rust's own numeric parsing capabilities when applied to JSON's specification. This isn't a minor oversight; it impacts the fundamental interpretation of numeric data within JSON structures, which is a cornerstone of data exchange.

Unexpected JSON Number Parsing Behavior

RFC 8259, the standard for JSON, is precise about what constitutes a valid number. It defines a number as an integer part, optionally followed by a fractional part and/or an exponent part. However, the Rust developer discovered that Rust's standard library number parsing logic, when applied to JSON strings, incorrectly validates several formats that are explicitly forbidden by the RFC. These include values like NaN, Infinity, -Infinity, numbers starting with a decimal point without a leading zero (e.g., .5), numbers ending with a decimal point (e.g., 5.), and numbers with explicit plus signs (e.g., +1).

These are not edge cases; they are explicit violations of the JSON standard. A parser that accepts them is, by definition, not conforming to the RFC. The implications are significant: data serialized according to strict JSON standards might be incorrectly processed by a Rust application relying on these standard library parsers, leading to unexpected behavior or data corruption. This is particularly concerning for applications that exchange data with other systems, where strict adherence to standards is crucial for interoperability.

The surprising detail here is not the percentage, but the *nature* of the accepted invalid formats. These are not subtle ambiguities; they are direct contraventions of documented standards. It suggests a potential disconnect between the general-purpose numeric parsing functions available in Rust's standard library and the specific, more constrained requirements of the JSON specification.

Rust code snippet demonstrating invalid JSON number formats accepted by the standard library

Building Without Dependencies: The Trade-offs

The hackathon constraint of zero dependencies forces developers to confront the capabilities and limitations of the standard library. While Rust's `std` is robust, it is designed for general-purpose programming. It provides foundational tools, but not specialized validation logic for every conceivable data format. For JSON, this means that while Rust can parse numbers in a general sense, it doesn't automatically enforce the stricter rules required by the JSON RFC.

Building a compliant JSON parser from scratch without libraries like serde_json or simd-json is a substantial undertaking. It requires careful implementation of the state machine that navigates the JSON structure, correctly handles string escaping, distinguishes between objects, arrays, primitives, and manages the nuances of numeric representation. The discovery regarding number parsing highlights that even seemingly straightforward components like number conversion require careful attention to specification details.

This exercise serves as a potent reminder that standard libraries, while essential, are not always bespoke solutions for every problem. They provide building blocks. When those blocks are used for specific protocols like JSON, developers must ensure that the assembled structure adheres to the protocol's rules, even if the individual components don't inherently enforce them. For developers working with Rust and JSON, this means any custom parser or even careful use of standard library functions for number conversion within a JSON context needs explicit validation against RFC 8259.

What This Means for Rust Developers

For developers building new applications in Rust, the takeaway is clear: do not assume that Rust's standard library number parsing is inherently compliant with JSON standards. If you are implementing your own JSON parser or processing JSON data where numeric integrity is paramount, you must add explicit checks for formats like NaN, Infinity, and numbers with leading/trailing decimal points or leading plus signs. This might involve augmenting the output of standard parsing functions with custom validation logic.

The existence of this discrepancy also raises questions about the broader ecosystem. While established libraries like serde_json are known for their compliance, relying on lower-level or custom parsing logic without rigorous validation could introduce subtle bugs. Developers should be aware of the potential pitfalls when deviating from well-vetted, community-standard parsing libraries. The 72-hour challenge, while successful in its constraint, has illuminated a specific area where Rust's standard library, in its general-purpose role, falls short of a specific data interchange standard.

What nobody has addressed yet is whether this behavior is intentional, a known limitation, or an oversight that might be rectified in future standard library versions. For now, developers must be vigilant. If you're building a system that rigorously validates incoming JSON, you'll need to implement your own checks for numeric formats beyond what Rust's `std` provides out-of-the-box.