prettyplease: a minimal syn pretty-printer for generated Rust
A minimal `syn` syntax tree pretty-printer
At a glance
- What is it?
- prettyplease turns a syn syntax tree into formatted Rust source, and it exists for a specific reason: rustfmt is designed for code a human will read, and it gives up on code that is hard to format, which is exactly what generated code is. The crate trades a small amount of formatting quality for a formatter that never bails out and compiles in seconds.
- Who is it for?
- prettyplease fits a project that generates Rust source, whether from bindgen, from a macro expansion, or from your own code generator, and that needs the output to be readable and to be formatted identically on every machine. It does not fit a developer who wants their hand-written source formatted, because the crate is explicitly not intended for that and gives up the configurability rustfmt offers.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 97 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A printer for code nobody will read line by line
prettyplease is a minimal syn syntax tree pretty-printer. The README puts it in one line: it is a pretty-printer to turn a syn syntax tree into a String of well-formatted source code, and in contrast to rustfmt, this library is intended to be suitable for arbitrary generated code.
That last clause is the whole product decision, and everything else in the README is a consequence of it.
The reason rustfmt is the wrong tool for generated code is described carefully. Rustfmt prioritizes high-quality output that is impeccable enough that you would be comfortable spending your career staring at its output, and that means some heavyweight algorithms, and it has a tendency to bail out on code that is hard to format, with a link to a specific rustfmt issue and a note that there are dozens more issues like it.
The author's argument for why that matters is the interesting part. For human-generated code the behaviour is not a big deal, because when code gets highly nested, the human is naturally inclined to refactor into more easily formattable code. But for generated code, having the formatter give up leaves it totally unreadable.
That reframing is what makes the crate sensible rather than merely cheaper. The person reading generated output is usually a person debugging a macro expansion or reviewing a diff, not a person maintaining the file, so the requirement is that the output be legible and consistent, not that it be beautiful.
The library is designed using the simplest possible algorithm and data structures that can deliver about 95% of the quality of rustfmt-formatted output. The crate is Rust, the licence is MIT OR Apache-2.0, the minimum supported Rust version is 1.71, and the current version is 0.3.0.
What a rustfmt bail-out looks like
The README shows the failure mode concretely, and it is worth looking at because the symptom is strange the first time you see it.
The examples directory holds an input file and three outputs, one from prettyplease, one from rustfmt, and one from the rustc pretty-printer, all generated from the same input. The claim is that if you were not told which output file was which, it would be practically impossible to tell, except for one line in the rustfmt output, line 435, which is more than a thousand characters long because rustfmt just gave up formatting that part of the file.
match segments[5] {
0 => write!(f, "::{}", ipv4),
0xffff => write!(f, "::ffff:{}", ipv4),
_ => unreachable!(),
}The line that follows the match block in the rustfmt output is the whole of the remaining code on one line, a derive, a struct, a loop, a function, all of it, with spaces around every token where a space is not strictly needed. It is what a formatter looks like when it refuses to make a decision and dumps the syntax tree back out in the most literal way possible.
The README calls this a pretty typical manifestation of rustfmt bailing out in generated code, where a chunk of the input ends up on one line.
The other manifestation is the one you may have hit by hand. You are working on some code, running rustfmt on save like a conscientious developer, and after a while you notice it is not doing anything. You introduce an intentional formatting issue, like a stray indent or semicolon, and run rustfmt to check your suspicion. It does not get cleaned up, because rustfmt is not formatting the part of the file you are working on.
prettyplease is designed to have no pathological cases that force a bail out. The entire input you give it will get formatted in some good enough form. That guarantee is the feature.
How close the output actually is
The quality claim is quantified, and the numbers are the most useful thing in the README for anyone deciding whether to use the crate.
The library is designed using the simplest possible algorithm and data structures that can deliver about 95% of the quality of rustfmt-formatted output. In the author's experience testing real-world code, approximately 97 to 98% of output lines come out identical between rustfmt's formatting and this crate's. The rest have slightly different linebreak decisions, but still clearly follow the dominant modern Rust style.
Two different numbers are doing work there, and they are measuring different things. The 95% figure is a judgement about quality, which is subjective, and the 97 to 98% figure is a measurement of agreement, which is not. The agreement number is the more useful one in practice, because what actually causes churn in a repository is a formatter that disagrees with a checked-in file, and a formatter that differs on two percent of lines by choosing a different line break is a formatter whose diffs are small.
The author also frames the intended audience explicitly. The trade-offs made by this crate are a good fit for generated code that you will not spend your career staring at, with the output of bindgen and the output of cargo-expand given as the examples. In those cases it is more important that the whole thing be formattable without the formatter giving up, than that it be flawless.
That is a deliberate, narrow, and defensible scope, and it is worth checking your own use case against it. If your generated file is something a reviewer reads during a code review, the two percent of lines that differ are the lines a reviewer will look at. If it is a file that a compiler consumes, the difference does not matter at all.
The repository also contains a fuzz directory and a cargo-expand directory with its own update workspace member, which is consistent with the claim that the author uses this crate in real tooling rather than only describing it.
The feature matrix against rustc and rustfmt
The comparison table in the README is the most concise summary of the project, and every row is a decision you might make differently.
On non-pathological behaviour on big or generated code, prettyplease gets the green mark and both rustc and rustfmt do not. On idiomatic modern formatting, described as locally indistinguishable from rustfmt, prettyplease and rustfmt get the mark and rustc does not.
Throughput is quoted as 60 MB/s for prettyplease, 39 MB/s for rustc, and 2.8 MB/s for rustfmt.
The dependency counts are 3 for prettyplease, 72 for rustc, and 66 for rustfmt, and the compile time including dependencies is 2.4 seconds, 23.1 seconds, and 29.8 seconds respectively.
Then three rows where the marks go the other way. prettyplease is buildable using a stable Rust compiler, and neither rustc nor rustfmt are. prettyplease is published to crates.io, and neither of the others are. And prettyplease is not extensively configurable, and is not intended to accommodate hand-maintained source code, while rustfmt is both.
The last row is the one to read twice, because it is the row where prettyplease deliberately loses. This is not a rustfmt replacement, it is a different tool for a different job, and the table says so plainly rather than pretending to compete on the same ground.
The numbers about dependencies and compile time matter more than they look for a build tool. A crate that is a dependency of a code generator, which is itself a dependency of a build script, adds its dependency tree to every downstream compile. Three dependencies and two and a half seconds is a very different proposition from 72 dependencies and 23 seconds, and the difference is paid by everyone who builds the tool, not only by the author.
Integrating a formatter without shelling out
There is a second argument in the README about integration that is independent of the formatting quality, and it is the one that usually decides the matter for a tool in a build script.
Rustfmt can be problematic to integrate into projects, for two reasons. It is written using rustc's internal syntax tree, so it cannot be built by a stable compiler. And its releases are not regularly published to crates.io, so in Cargo builds you would need to depend on it as a git dependency, which precludes publishing your crate to crates.io as well.
That second point is a hard blocker for a specific and common case. If you are writing a crate that generates code, and you want that crate to be publishable, you cannot take a git dependency on rustfmt, so you cannot use rustfmt as a library. You can shell out to a rustfmt binary, and the README says why that is bad too: that is whatever rustfmt version is installed on each developer's system, if any, which can lead to spurious diffs in checked-in generated code formatted by different versions.
The spurious diff problem is the one that bites. If your project checks in generated code, and the formatting depends on which machine regenerated it, then every developer with a different toolchain produces a different diff, and reviewing those diffs becomes noise.
prettyplease is designed to be easy to pull in as a library, and it compiles fast. Three dependencies, published to crates.io, buildable on a stable compiler, and the same version on every machine.
The comparison against the rustc pretty-printer is in the same spirit, with its own input and output files in the examples directory, and the README notes that this is the printer that gets used when rustc prints source code, such as with the unpretty expansion flag. The rustc one is not buildable on stable, is not published, and is markedly slower, so it is the least practical of the three for a library author.
The package, its one feature, and its licence
The Cargo.toml is small enough to read in full, and it says most of what you need to know before depending on the crate.
The package is named prettyplease, version 0.3.0, with edition 2021 and a rust-version of 1.71. The description is a minimal syn syntax tree pretty-printer, the category is development-tools, and the keyword is rustfmt.
The dependencies are two in practice, proc-macro2 at 1.0.80 with default features off, and syn at version 3 with default features off and the full feature enabled. The README's table counts three dependencies, and proc-macro2 and syn are the two declared directly in Cargo.toml.
There is exactly one feature, called verbatim, and it enables the parsing feature of syn. That is the switch to reach for if you need to parse source into a syntax tree in the first place rather than only printing a tree you built programmatically.
The dev dependencies are the interesting part for anyone reading the source. Syn is pulled with the clone-impls, extra-traits, printing, and visit-mut features in addition to parsing, which tells you the test suite does not only print trees but also mutates them and clones them, and the indoc and quote crates are there to write the expected output and the test inputs without fighting indentation.
The package also declares a links key, prettyplease03, which reserves a native library link name so two versions cannot be linked into the same binary, a detail that matters for a crate that ends up in a build script.
The licence is MIT OR Apache-2.0, which is the dual licence Rust projects use so that the choice can be made per consumer rather than per project. The repository carries both licence files.
On maintenance, the last push was on 2026-07-18 and the current release is 0.3.0, published the same day. The two releases before it are 0.2.37 from 2025-08-19 and 0.2.36 from 2025-07-24, so the version line moved to 0.3.0 in July 2026 after a year of patch releases, which is a slow and steady rhythm for a crate whose whole surface is one function.
Editorial conclusion
prettyplease fits a project that generates Rust source, whether from bindgen, from a macro expansion, or from your own code generator, and that needs the output to be readable and to be formatted identically on every machine. It does not fit a developer who wants their hand-written source formatted, because the crate is explicitly not intended for that and gives up the configurability rustfmt offers. Verify first that the crate version you pin matches the syn version you use, since the API follows syn's own, that you enable the verbatim feature only if you need the parsing support, and that you compare the output against rustfmt on a sample of your real generated files, since the crate is a library and the formatting differences are decided by its algorithm rather than by configuration.
Frequently asked questions
What is prettyplease used for?
It turns a syn syntax tree into a String of well-formatted Rust source. It is intended for arbitrary generated code, such as the output of bindgen or cargo-expand, rather than for hand-maintained source, which is the opposite of rustfmt's purpose.
How does prettyplease differ from rustfmt in quality?
The README says the crate is designed to deliver about 95% of the quality of rustfmt output, and that in the author's experience testing real-world code, approximately 97 to 98% of output lines come out identical between rustfmt and this crate, with the rest having slightly different linebreak decisions that still follow the dominant modern Rust style.
Why not just depend on rustfmt?
Because rustfmt is written using rustc's internal syntax tree and cannot be built by a stable compiler, and its releases are not regularly published to crates.io, so a Cargo build would need a git dependency, which precludes publishing your own crate. Shelling out to a rustfmt binary gives whatever version is installed on each machine, which causes spurious diffs in checked-in generated code.
What are prettyplease's dependencies and how fast does it build?
The Cargo.toml lists proc-macro2 at 1.0.80 and syn at version 3 with the full feature enabled, and the README's comparison table puts the dependency count at 3 against 72 for rustc and 66 for rustfmt, with a compile time including dependencies of 2.4 seconds against 23.1 and 29.8 seconds.
Does prettyplease support the same configuration as rustfmt?
No, and the comparison table marks it as not extensively configurable, alongside not being intended to accommodate hand-maintained source code, which is where rustfmt is marked as the better tool. The one feature flag the crate offers is verbatim, which enables the parsing feature of syn.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dtolnay-prettyplease)Was this useful?