Why is the rand crate not included in the standard library, rather another dependency, despite it's requirements?
I have no idea really but I make the observation that over the course of computer history all kind of languages and all kind of operating systems have offered a rand() function of some kind and they have been found to be defective. Indeed infamous for their catastrophic design flaws, predictability, or structural biases. You can find examples in C++, Fortran, Javascript, PHP, Python, all kind of BASIC, Linux, Windows and BSD.
So I conclude it's a very good idea for Rust not to fix on any rand in its standard library.
Because libraries go to the std to die. It has not been obvious that the rand API is the right one to be stabilized and set in stone forever. The C++11 random API is a cautionary example that was seemingly well-thought-out and "the API that other random APIs want to be", yet it turned out to be suboptimal in several important aspects that are almost impossible to fix backwards compatibly.
There is now a very small randomness API in the nightly std which is in process to be stabilized. However, even this extremely minimal interface has several unresolved questions.
I am not sure if this question makes sense, but do all std targets and all targets that may support std in the future allow random number generation? what if a std target is perfectly deterministic?
rand supplies PRNGs for that purpose, which are fully deterministic if you know the internal state, along with the SeedableRng trait for setting the internal state of a PRNG.
For example, there's the ChaCha20Rng, which uses the ChaCha cipher to encrypt a stream of 64-bit counter values, using the seed as the cryptographic secret. If you can predict this PRNG's output without the seed, you've broken the ChaCha cipher it uses.
In case you didn't know, what they mean here is that std has strict backward compatibility requirements (it's been on the same SemVer version since 2015 and there are no plans to change that). That means even deprecated APIs have to be maintained forever.
With that in mind check out the changes rand has made over the years. The last two incompatible 0.x.0 releases being in January 2025 and February 2026.
rand has made 10 semver-major releases so far, refining its API to be safer, faster, more powerful. It turns out to be tricky to provide strong randomness + thread safety + performance + ergonomics at the same time!
in std the max semver-major releases is 1. It has to be right on the first try and then you have to live with it forever.
Perhaps it could be added now, assuming 10 iterations should be good enough, and by avoiding promising too much about specifics of the PRNG algorithms.
Rand can't be reliable, and Rust refuses everything not reliable. You won't find regex too.
I see it's version 0.10.3, now, so technically it should be considered unstable. From the README, is seems to be on purpose.
Versions
Rand is mature (suitable for general usage, with infrequent breaking releases which minimise breakage) but not yet at 1.0. Current
MAJOR.MINORversions are:
- Version 0.10 was released in February 2026.
Many crates seem shy to get to version 1.0, even when they're mature. I wonder if that's because Rust is considered more safe than many other languages.
It was extremely common in npm too... until npm changed the default from 0.1.0 to 1.0.0 for a new project. Sometimes it's the dumbest little things...
At the risk of side-tracking even more, I believe that many crates are afraid of rolling breaking changes. Semver was introduced to be able to distinguish different types of changes, not to discourage major releases. In my experience software is always just a best guess at the moment it was written to solve a specific problem. Changes to ecosystems, requirements or new insights may lead to the necessity of changes, even breaking ones. This, imho, is not something bad, but rather something that is occasionally necessary, which is why I am wary of remarks like
I am aware that this is not a single person's opinion here, but still, I think that this notion from the Rust dev team attempts to bypass reality.
The flip side of breaking changes is that every time you make a breaking change, all code that worked before the change has to be checked for compatibility with the change. Often the compiler can help with this, but it's still a nuisance if your code that used to compile no longer does after updating to the latest version of a dependency - and it's more of a problem if updating to the latest change compiles, but no longer works as you expected.
How big a problem this is depends on how "foundational" your code is; a leaf crate like companies-house-api with very few dependents (none public) doesn't see much of a problem with breaking changes - the cost of checking and applying needed fixes after a breaking change is low, and so a breaking change doesn't have to be that much of a win to be a net gain.
At the other extreme, core is completely foundational. If core has any breaking changes, then all Rust code needs to be checked and any necessary fixes applied after the change. This is a huge amount of work - the cost of checking and applying needed fixes after a breaking change is very high, and as a result, a breaking change has to be a huge benefit to be worth making, otherwise it's a net loss for the ecosystem (see the Python 2 to Python 3 transition for an example, which took 12 years just to be declared "done", and which still isn't finished in the wider ecosystem, with some people still sticking to Python 2.7.10 because they won't pay the cost of the Python 2 to 3 breaking change).
And that, not SemVer, is what pushes back hard on breaking changes; the cost of a breaking change goes up as you affect more code, and the cost of maintaining two or more branches indefinitely so that your dependents can catch up with the breaking change is also significant. A consequence of this is that the value of a breaking change has to get bigger as you affect more dependents - and as std affects a majority of Rust code in the wild, a breaking change has to have huge value to be worth the cost.
I think that this notion from the Rust dev team attempts to bypass reality.
There are technical reasons for why std can't break. When passing an Option from one crate to another they both must be the same. The crates ecosystem sidesteps this problem by letting you have more than one crate version in your tree, but the way things are set up today there can be only one std. I do think that a stdx crate approach would be reasonable. There have been a few attempts, that have petered out for one reason or another.
Honestly maybe the biggest problem with having stuff in std is that it causes people to think more stuff should be in std (eg, this topic!)
If Rust had/could have gone a more Deno approach where the "standard library" was just a crate no different to any other crate we wouldn't have this expectation that the language is "deficient" if you have to get a crate for some feature: you have to get a crate for every feature! (Theoretically it would also allow breaking changes with a bit less pain, if you were real careful about how it was implemented so multiple versions of std could work together.)
It's rather late for that change, though ![]()
Yes,I suppose it makes sense not to put too much in std, and it's not an easy balancing exercise, either.
I think that what we miss is an "official" extension or rather a group of crates that have a large common interest, like RNG, regular expressions, extra iterator adaptors, error management, serdes, ... There are known crates for all of those, of course, but I think it's confusing for programmers who are not used to it, especially when there are several potential crate candidates and it's not easy to assess them.
I suppose it just requires some getting used to and some time to get to know the ecosystem.
A programmer new to Rust needs random numbers (for example) and does not know how. Typically today we would expect he goes to google and finds out. How is that more or less confusing if what he finds is in the standard library or in a crate on crates.io?
There is the issue of adding an entry to Cargo.toml but that is almost nothing and an LLM user will not even notice.
Should std omit functionality to prevent people doing stupid things? I'm not sure. Virtual every function from std can be used to do stupid things in some ways: A Vec can be filled until OOM, using std::fs important files can be modified and deleted, etc.
For many use cases, a simple, flawed pseudo random generator seeded with the current time is good enough. Maybe naming the method pseudo_random() or something like this would be a hint to users, there might be an issue with the generated numbers.
It would be good to remove std::fs from libstd, because that design can't work with Web FileSystem API and can't support capability-based security and has problems with TOCTOU due to lack of handles for directories. If it was a crate, users could choose a more appropriate version of the API, or stay on the basic 1.0.
A related proposal:
I think HashSet, HashMap, VecDeque, BinaryHeap should not be in std.
BTreeSet can do BinaryHeap's job, and if you wanted a really optimized priority queue it wouldn't be a binary heap (that's not cache efficient).
BTreeMap can for the most part do what HashMap does without its hashing choices and randomness.
All the basic collection needs are met by Vec and BTreeSet/BTreeMap.