Often people new to the Rust programming language will ask “why isn’t basic functionality like random number generation/regular expressions/datetime/serialisation in the standard library? Why do I need to import a third party library of unknown quality for these features?” I see this around once a month on the Rust subreddit (examples one, two, three, four), so I thought it’d be worth addressing.
Reasons to keep the standard library small
- It’s not easy to add code to the standard library. The tracking issue to add
once_cell
to the standard library shows why. It takes time and effort to consider the pros and cons of naming, code layout and implementation details and more time to address the concerns raised. It’s been 1.5 years since that issue was opened and it could be a while before it’s closed. Remember that these are people donating their time so it’s not reasonable to expect them to work to strict deadlines. And it’s completely fair for reviewers to want to be absolutely sure about the API because - It’s impossible to remove code from the standard library once it’s added. The Rust language’s stability guarantee states that if code compiled with any version of the standard library, it will continue to do so in future with minimal hassle. In other words, no breaking changes. For example, fixing
std::env::home_dir
would be a breaking change so it’s marked deprecated, with a suggestion to use a crate from crates.io. There’s a reluctance to add APIs that could be forced to change in the future. This isn’t great because - It takes time to find the right interface. Rust is a relatively new language. It introduces novel constraints that make finding the right interface difficult. Prior art from other languages might not be helpful. The best way to find that API is by releasing a library and iterating based on feedback. If it’s possible to improve the interface, issue a new release with a breaking change.
rand
0.8 (2020) is better and more secure thanrand
0.2 (2015) but it took time, effort and several breaking changes to get there.rand
’s changelog has 29 instances of “remove”, 9 of “replace” and 15 of “deprecate”. Since the Rust package manager respects semantic versioning, new releases were opt-in and didn’t break anyone’s code. The standard library, which has one version by design, couldn’t support this experimentation. - There may be more than one way to solve a problem.
rand
andserde
might seem like no brainers to add to the stdlib, but they are not the only way or the optimal way to solve the problems they tackle. Depending on the context, a user might prefernanorand
orminiserde
. There’s no better or worse between these crates, just different trade-offs. And other times, it might seem like a problem is “solved”, when a better solution comes along.lazy_static
was the de facto standard for lazy initialisation, untilonce_cell
was published. The standard library picking a solution blesses that approach, making it less likely that someone will develop an alternative or that alternative will gain momentum. - We have the Internet now. This was not a given for languages developed before 2000. A large standard library is a killer feature when the developer might not have Internet to find and download third party dependencies. But with Internet access + a package manager + a package repository, it’s trivial to download dependencies. Rust benefited from having those in place since before 1.0. Today all public Rust code builds with
cargo
and publishes to crates.io. Even older languages are adopting this approach. Rather than addrequests
to Python’s large standard library, the documentation encourages the user to download it themselves.
Many of these boil down to giving library authors a better experience. The combination of #1, #2 and #3 mean some authors prefer working outside of the constraints of the standard library. Andrew Gallant (aka Burntsushi), the maintainer of regex
said “I don’t want regex
in std. I want it as its own project where it can evolve and breathe on its own.” Rust is still a relatively small language where a handful of authors maintain many important libraries. If their experience developing libraries is positive, it increases the likelihood that the ecosystem will continue to grow. All users benefit from a thriving ecosystem.
Reasons to make it larger
- Discoverability for new users. People who are new to Rust are afraid of pulling in untrusted code, with good reason. Number of downloads from crates.io is a reasonable proxy for quality/reliability, but not a perfect one. Newbies don’t have access to the main shortcut experienced Rust programmers do - recognising the names of trusted authors and libraries. There are a couple of community driven workarounds:
- The Rust cookbook, which provides solutions to commonly encountered problems (“how do I process text/make a web request?”). It’s kept updated, but it’s not easily discoverable. For example, the Rust book doesn’t link to it. Coming from search results, a new user might not know whether the cookbook itself is reliable.
- StackOverflow questions. StackOverflow is usually at the top of search results. Thanks to the efforts of several dedicated community members, not only are most Rust questions answered, but are also kept up to date with evolving best practices.
- Security. There’s more scrutiny and ceremony for changes to the standard library. Although it means more time and effort for the authors of those changes, users can rest assured the changes are legit. Stdlib dependencies are much less likely to be hijacked or yanked or silently broken. Code that can be built locally without connecting to the Internet is easier to vet and trust. Workarounds:
- Check the
Cargo.lock
file into version control for deterministic builds. Only for applications, not libraries - Cargo FAQ. - Track security vulnerabilities in the dependency tree -
cargo-audit
. - Contribute to and benefit from other people reviewing dependencies -
cargo-crev
.
- Check the
- Licensing. 80% of Rust crates are permissively licensed under Apache 2.0 or MIT or both, similar to the dual-licensed standard library. However, if an author chose a non-standard license like WTFPL or similar, that could be a tough sell to adopt in a work environment.
- Track all licenses of dependencies -
cargo-license
.
- Track all licenses of dependencies -
- Maintenance. Individual authors can and do sometimes stop maintaining their crates. Sometimes they don’t hand over publishing rights to their repos. This is less of a concern for standard library code, where enough people will have the permissions and (hopefully) at least one person will be motivated enough to fix reported bugs.
- Encourage maintainers to reduce their bus factor.
- Thank them for their work :)
All of these are legitimate concerns and the listed workarounds don’t fully solve them.
Alternatives
One possibility is for the Rust project to create a super-library that contain commonly used, vetted crates that work well together. This approach has seen some success in other languages like Haskell.
The stdx project tried to be “the missing batteries of Rust” but didn’t gain much traction. The main issue is increased compile times for little benefit. Once you’ve decided to use one of the constituent libraries, you might as well import it directly and avoid compiling the others.
I’m not aware of any other alternatives.
Will Rust change?
In my opinion, it’s not likely. The current approach seems to work well. Rust is gaining in popularity and adoption. It remains well loved in developer surveys. The current consensus is that it ain’t broke.
More importantly, a Rust that decided to ship a large standard library would be a different language, one that might not work as well for it’s current users.
Thanks to Andrew Gallant for the history lesson on Rust’s early days and sharing his perspective as a library author.