https://github.com/tdymel/dfmt
std::fmt like dynamic template formatting with drop in replacement macros and a fully featured API
https://github.com/tdymel/dfmt
dynamic dynamic-templates formatting runtime rust string
Last synced: 6 months ago
JSON representation
std::fmt like dynamic template formatting with drop in replacement macros and a fully featured API
- Host: GitHub
- URL: https://github.com/tdymel/dfmt
- Owner: tdymel
- License: apache-2.0
- Created: 2025-12-16T17:22:50.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2025-12-29T10:42:12.000Z (7 months ago)
- Last Synced: 2026-01-13T22:54:41.538Z (6 months ago)
- Topics: dynamic, dynamic-templates, formatting, runtime, rust, string
- Language: Rust
- Homepage: https://crates.io/crates/dfmt
- Size: 108 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE-APACHE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# dfmt - `d`ynamic `format!`
`dfmt` provides `core::fmt`-like formatting for **dynamic templates** and is a **fully featured** dynamic drop in replacment for the macros: `format!`, `print!`, `println!`, `eprint!`, `eprintln!`, `write!`, `writeln!`.
```rust
// Check out the documentation for a complete overview.
use dfmt::*;
let str_template = "Hello, {0} {{{world}}} {} {day:y` |
| Sign | `+`, `-` |
| Alternate | `#` |
| Zero-padding | `0` |
| Width | `{:20}`, `{:width$}` |
| Precision | `{:.5}`, `{:.precision$}`, `{:*}` |
| Type | `?`, `x`, `X`, `o`, `b`, `e`, `E`, `p` |
| Argument keys | `{}`, `{0}`, `{arg}` |
## How it works
* If the template is a literal, then the `format!` macro is used under the hood.
* Uses the `core::fmt` machinery under the hood. Therefore, you can expect the same formatting behaviour.
* It uses [black magic](https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html) to provide a comfortable macro.
## Safety
There are multiple runtime checks to prevent you from creating an invalid format string.
* Check if the required argument value exists and implements the right formatter.
* Check for duplicate arguments
* Validate the template
## Performance
In the best case `dfmt` is as fast as `format!`. In the worst case, its up to 60% - 100% slower.
However, I believe with further optimization this gap could be closed. In fact, with the `formatting_options` feature we are even faster in some cases.
### Considerations
* While the template parsing is fast, you can just **create it once and then reuse it** for multiple arguments.
* There is a **unchecked** version, which skips safety checks.
* If the template is a literal, it will fall back to **format!** internally if you use the macro.
### Overhead
* When creating the `Arguments` structure, a vector is allocated for the arguments. This is barely noticeable for many arguments.
* Right now padding a string with a fill character will cost some overhead.
* If a pattern reuses an argument multiple times, it will push a typed version of this value multiple times right now. This allocates more memory, but is required to provide a convinient API.
* The macros for convinience, incour some overhead due to auto-deref specialization. Its about 5-10 ns per value.
### Nightly
If you are on nightly, you can opt in to the `nightly_formatting_options` feature to further improve the performance,
especially for the fill character case and to reduce compilation complexity.
### Benchmarks
These benchmarks compare `dfmt` with `format!` with dynamic arguments only. Obviously, if `format!` makes use of const folding, it will be much faster.
#### Without `formatting_options` feature
| Benchmark | simple - 1 arg | simple - 7 args | complex |
| --------- | -------------- | --------------- | ------- |
| Template::parse | 67 ns | 249 ns | 684 ns |
| **format!** | **30 ns** | 174 ns | **515 ns** |
| Template unchecked | 46 ns | **173 ns** | 845 ns |
| Template checked | 49 ns | 250 ns | 911 ns |
| dformat! unchecked | 51 ns | 235 ns | 952 ns |
| dformat! checked | 51 ns | 260 ns | 1040 ns |
#### With `formatting_options` feature
| Benchmark | simple - 1 arg | simple - 7 args | complex |
| --------- | -------------- | --------------- | ------- |
| Template::parse | 67 ns | 249 ns | 684 ns |
| **format!** | **30 ns** | 174 ns | 515 ns |
| Template unchecked | 46 ns | **169 ns** | **464 ns** |
| Template checked | 49 ns | 238 ns | 527 ns |
| dformat! unchecked | 51 ns | 232 ns | 576 ns |
| dformat! checked | 51 ns | 257 ns | 658 ns |
## Minimal rustc version
Right now it compiles until 1.81, this is when `std::error` went into `core::Error`.
You can opt out of `error`-impl by disabling the feature `error`. Then you can go down until 1.56.
## License
This project is dual licensed under the Apache 2.0 license and the MIT license.