Smart Format
Composable, zero-allocation Display formatting for Rust.
smart-format provides extension traits that add formatting operations to any type implementing
Display. Each operation returns a lightweight wrapper that itself implements Display, so
operations compose through fmt::Formatter without intermediate String allocations.
Why this exists
Rust's Display trait is a streaming, zero-alloc formatting interface. But composing Display
values is awkward — there's no built-in vocabulary for "escape this, then truncate, then wrap in
tags." People end up calling .to_string() at every step, allocating intermediate Strings that
only exist to be formatted again.
This crate makes the composition part zero-alloc too. Every operation is a struct wrapping
T: Display that implements Display with a transformation. Chain as many as you want.
Features
Core functionality (always available):
| Trait | Methods |
|---|---|
SmartFormat |
display_wrap(), display_prefix(), display_suffix(), display_if(), display_or_if(), display_truncate(), display_truncate_with(), display_pad_left(), display_pad_right() |
SmartFormatCase |
capitalize(), lowercase(), uppercase() |
DisplayIterator |
display_concat(), display_join(sep) |
Feature-gated transforms:
| Feature | Trait | Methods |
|---|---|---|
html |
SmartFormatHtml |
html_escape(), html_escape_strict(), html_unescape_basic(), html_attribute(name) |
url |
SmartFormatUrl |
url_escape() |
xml |
SmartFormatXml |
xml_c_data() |
full |
— | enables all of the above |
Usage
[]
= { = "0.1", = ["html"] }
use *;
// Combinators chain without allocation
let output = "hello world"
.display_truncate
.display_pad_right
.display_wrap
.to_string;
assert_eq!;
// Case transforms
let title = "hello world".capitalize.to_string;
assert_eq!;
// Iterator formatting
let joined = .iter.display_join.to_string;
assert_eq!;
// Left-pad (zero-alloc, two-pass)
assert_eq!;
With the html feature:
use *;
// Escape, truncate, wrap — all composed, zero intermediate allocations
let safe = "<script>alert('xss')</script>"
.html_escape
.display_truncate
.display_wrap
.to_string;
Design decisions
Zero-alloc by construction. Every combinator is a struct wrapping T: Display that implements
Display. No heap allocation happens until someone calls .to_string() or writes to a sink.
This is the same pattern used by Iterator adapters.
Feature flags for domain-specific transforms. HTML/URL/XML escaping adds dependencies and code that many users don't need. Core text operations (case, iteration) are always available because they have no extra dependencies and are universally useful.
Unicode scalar values for width. Width-sensitive operations (future: pad, truncate) count
Unicode scalar values (char), not bytes or grapheme clusters. This is documented as a known
limitation. Grapheme-aware and terminal-width-aware variants may be added behind a feature flag.
Streaming escaping via bitset. HTML escape uses a u128 bitmask for O(1) "needs escaping?"
per ASCII byte. The bitmask is computed at compile time. Non-ASCII bytes pass through unchanged.
MSRV
Rust 1.85.0 (edition 2024).
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Development
Recommended (enable repo hooks once per clone):
Quality gates:
See also: CONTRIBUTING.md.