5 releases (3 breaking)
0.4.1 | Jan 3, 2024 |
---|---|
0.4.0 | Jan 1, 2024 |
0.3.0 | Feb 12, 2023 |
0.2.0 | Jul 1, 2022 |
0.1.0 | Apr 15, 2022 |
#33 in Value formatting
1.5MB
37K
SLoC
debug3
A space effiecent replacement for std::fmt::Debug
The Pitch
Lets say you have data that looks like this:
let complex_structure = vec![
vec![None, Some(2)],
vec![Some(2), None],
vec![Some(4), Some(777)],
vec![None, Some(2)],
vec![Some(2), None],
vec![None, None, None, None, None],
];
And you want to format it as a string. You could use format!("{:?}", complex_structure)
and get something like
[[None, Some(2)], [Some(2), None], [Some(4), Some(777)], [None, Some(2)], [Some(2), None], [None, None, None, None, None]]
But this is too much one one line, and is hard to read. And it gets worse for larger structures.
Fortunaly theirs an alternative format!("{:#?}", complex_structure)
, which gives
[
[
None,
Some(
2,
),
],
[
Some(
2,
),
None,
],
[
Some(
4,
),
Some(
777,
),
],
[
None,
Some(
2,
),
],
[
Some(
2,
),
None,
],
[
None,
None,
None,
None,
None,
],
]
This has the oposite problem, where it uses too much space, even when the code could be packed denser.
debug3
provides a third option that is denser than :#?
but more readable than :?
. If you use debug3::pprint(complex_structure)
, you get
[
[None, Some(2)],
[Some(2), None],
[Some(4), Some(777)],
[None, Some(2)],
[Some(2), None],
[None, None, None, None, None],
]
Overview
The main entrypoint is the Debug
trait, which is the equivalent to std::fmt::Debug
, and has a similar API.
This can be either #[derive]
d, or implemented manually.
use debug3::{Debug, Formatter, pprint};
#[derive(Debug)]
struct MyStruct {
a: i32,
b: i32,
}
struct AnotherStruct {
a: i32,
b: i32,
}
impl Debug for AnotherStruct {
fn fmt(&self, f: &mut Formatter) {
f.debug_struct("AnotherStruct")
.field("a", &self.a)
.field("b", &self.b)
.finish()
}
}
assert_eq!(pprint(MyStruct { a: 1, b: 2 }), "MyStruct { a: 1, b: 2 }");
assert_eq!(pprint(AnotherStruct { a: 1, b: 2 }), "AnotherStruct { a: 1, b: 2 }");
Once your type implements Debug
, you have several options to format it
Comparison to std::fmt::Debug
:
While the main advantage of debug3
is the superior output quality, it has several drawbacks compared to std::fmt
that you should know
- Commonness: Virtually every type in Rust implements
std::fmt::Debug
, vitrualy no types outside of [std
] implementdebug3::Debug
. - Availibility:
std::fmt
is also availible ascore::fmt
, which allows you to use it inno_std
environments.debug3
requires several allocated data structures, so cannot support these environments. - Versitility:
std::fmt::Formatter
has many more API's for implementingstd::fmt::Debug
. In order to achive nice formatting, we cannot accept arbitrary strings, but must have items in the form of Structs, Tuples, Maps, Lists and Sets. - Configurabiliy: We dont suport stuff like
format!("{:x?}, 1)
to configure how numbers are printed. - Ease of use: We don't have a macro like
std::format
to easily create a string from several elements which implementDebug
Prior Art
debug3
would not be possible without all of the following excelent work
core::fmt
- The public API ofFormatter
and[builders]
prettyplease
- Most of the prety printing algorithm is lifted directly from this cratecustom_debug
- The derive macro forDebug
is based on this crate.- python's
pprint
- Inspiration for this type of formatting for debug output.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Dependencies
~0.3–23MB
~310K SLoC