Let's face it, what will your average dev do in the absence of something being in stdlib ?
They will (semi)randomly pick some third-party crate which in all likelihood is totally unvetted.
The crate that they selected could equally turn stale and outdated quickly if its a small one-man-band Github party (a not unfamiliar story these days).
Hence, having a standardised baseline available in stdlib is no bad thing IMHO. It brings "sane defaults" and forces anyone wanting to do different to make a thought-out decision, not just grab whatever Crate "looks good" so they can get on with coding.
Coming from Ruby, there are lessons to be learned. Ruby has a large stdlib, like you seem to want. But a lot of stuff in there is outdated. So much, that many, even most, people, projects, frameworks immediately replace them with a community version. Sometimes because they are actually better maintained, more stable, faster, cleaner and more secure than the ones in stdlib. A similar thing is seen in Ruby-on-Rails, which adds features and even entire frameworks with every release, but which many people swap out for their own preferred lib almost immediately.
This is inevitable and obvious. And, IMO should be embraced: deprecate them from stdlib, and leave it to "the community".
> They will (semi)randomly pick some third-party
Again good lessons can be learned from Ruby (gems) here. People will pick a "random" one only if there are no canonical, defacto-standard libs (gems) out there. But for some gems there is a clear, singular goto option and everyong will pick that, turning it into something of a stdlib. A good next step would, IMO, NOT be to then pull those libs into the stdlib, but, instead, to make the "not-fully-stdlib-but-also-not-random-community-provided" into a distinct category of libs.
And is very aggressively moving stuff out of stdlib to three concentric circles of gems; “default gems” which are almost like stdlibs (and still tied somewhat to Ruby versions) but are separately maintained and can be updated between Ruby versions, “bundled” gems that are usually distributed with Ruby, and just plain regular gems.
That's okay though. It's better for some devs to (temporarily) pick some bad crates than to permanently include some broken legacy APIs in the standard library that need to stay there due to compatibility issues. Even Python 3 couldn't get rid of a lot of code that just should not be in the standard library.
The fact is that in practice, for most “standard” functionality that isn’t in the stdlib, there are well maintained, well vetted crates written by well known authors (or even developers of the stdlib). These are not hard to find, either by asking around or looking at what other crates use.
Rust’s stdlib is consistently of high quality. I can use everything in it without worry or concern. I can’t say the same about Python.
Keeping deprecated functions around still requires some maintenance burden, because you can’t break them just because they’re deprecated. That maintenance burden might become pretty extreme if we’re talking about something as complicated as an HTTP server module, as some folks are asking for in this thread.
I also personally don’t want to see the standard library littered with failed and deprecated experiments. In addition to the maintenance burden, it adds confusion when you’re learning the language or trying to discover how to do something.
Yes, but the crate dev likely offers no maintenance guarantees.
A bad stdlib had to be maintained forever (and potentially divert new users from better options)
Code can be deprecated but it is often a long drawn out process.
It doesn't have to be that way, though. Why can't there be first-party crates? If the Rust core team is adding functionality to stdlib why can't they add functionality to a crate instead? Then you'd have a collection of first-party, vetted crates for devs to choose from.
The big difference is versioning (eg. you can have a crate that works on old compilers/stdlib, you can pull an older package version to new compiler/stdlib) and maintenance burden.
That this is the norm for a language that focuses on security is mind-boggling.
Didn't we learn anything from the NPM mess?
[1]: https://twitter.com/steveklabnik/status/1367854800193413126
Now there are reasons many people choose not to use the built in C++ std::map, and very nearly this ended up the case for Rust's HashMap, until they switched the implementation to the crate that was becoming popular. But even there's design decisions, like Rust using more compute to have a strong hash to avoid DoS attacks, that some may feel are inappropriate for their use cases.
In most GCed languages it wouldn't matter so much because you'd return a boxed `Iterator` or `Future` or what have you. But in Rust you generally want to avoid the allocation.
Ie, if I wanted an enum of either a string or an integer, it becomes "Either<&str, i64>".
But what I wanted is that it's either a string identifier or the database Id of something to be later referenced, which might be better described as "enum IdentOrDbId { Ident(String), DbId(i64) }".
This is of course a much simplified example of things I've faced.
fn some_func() -> impl SomeTrait {
if some_condition {
Either::Left(a)
} else {
Either::Right(b)
}
}
Both a and b will implement SomeTrait which is all callers care about. However, because they're structurally different they must be wrapped in an Either that delegates all methods from SomeTrait to a or b respectively.For object-safe traits where this would be possible, you can at least do this at the cost of an allocation, as you probably know:
fn some_func() -> Box<dyn SomeTrait> {
if some_condition {
Box::new(a)
} else {
Box::new(b)
}
}How could you ever possibly, gasp, write it yourself? Oh the horrors of computer science.
Seriously, many times I find myself needing only a fraction of some provided library.
In a compiled language like Rust, that should not matter, isn't it? The binary-size, performance or attack-surface should not increase from code that is unused as it won't be in the resulting binary. Or did I understand it completely wrong?
Maybe. I think .NET's core libs are excellent. So are Go's. I'm sure it's a maintenance burden, but man... It's so nice to be able to build a project with maybe 1-2 dependencies instead of 100s (as in my typical Node and Ruby projects, if you count transitive dependencies). It's hard to overstate how much of a strength I think a good stdlib is.
Even the Go standard library is quickly aging as contexts were not around when it was first created. It will be interesting to see how Go's standard library is going to evolve with Generics and more reliance on contexts.
I agree that we need a serious refactoring of Python's stdlib.
My hands start to sweat at the idea of this becoming The Python 4 Debacle.
If anything, I found go stdlib to age like fine wine. It doesn't do everything under the sun, but what it does it does very well and can easily be extended if needed.
There is no need to make Rust like Go, Go is already a great Go.
Philosophical reasoning like "Rust should have small stdlib" is horseshit. Ideal language has every function perfectly designed in the stdlib (kinda like PHP in 2003, lol). But to get closer to that dream you need some sustainable organization that maintains and expands that stdlib, wishes to do so and has resources to do that continuously.
If it's just random 5 people doing maintenance and hacking at their spare time, that's not enough for an expanding library of diverse code. That could be enough only for keeping very core features working/improving like const generics and borrow checker.
That way of thinking only makes sense if nothing ever changes. You even bring PHP as a good example here of how this fails fast. The world has changed greatly since 2003. The Python standard library carries three XML implementations and an outdated HTML parser irrelevant for the modern world. These libraries were state of the art when they were written, but it's the world around it that changed.
Now one can make the argument: why was it not modernized and updated? The simple answer is that people started building code against this and now depend on it working this way. The only way would be to completely rewrite them with new APIs, remove stuff no longer needed but then you're effectively back to just using crates.io and the likes. Except it's worse now because the standard library is a monolith and does not compose.
I agree you don't want everything in the stdlib, but havinga stdlib way to achieve something is pretty valuable, even if it's not the best way and never was.
In cases where libraries don’t keep up with changes in standards or whatever, yeah, you’re going to have to find another library. This is a part of our job that is pretty much unavoidable no matter the language. We should learn lessons like sticking dependencies you’re not certain about behind an interface to minimize the cost of switching if possible.
JSON library updates are a constant tedious chore for many in the Java world. Jackson and Gson have both had several we've needed to make over the years. Now, _most_ of these are due to non-default features where you allow it to deserialize arbitrary types that we don't use. But clients big enough to insist on a BOM and internal security teams both just see "Jackson version x.y.z has a RCE on it, you need to update", even if it's literally impossible to trigger without changing the application code to enable insecure features.
On the other hand, something like a json parser needing to change all the time to support the ecosystem is another good reason to keep it out of the stdlib, IMO. There are limited hands to work on Rust itself, and I’d rather they spend their time making improvements to the core of the language, stabilizing features, and so on. As a user of rust, I’m willing to pay the cost of finding a good json library if it means I don’t need to update rust versions just to deal with some issue in JSON parsing.
The thing is it’s easy to be wrong if you’re trying to predict up front what is going to wind up being stable and low maintenance and what isn’t, so the precautionary nature of what gets into the stdlib I think reflects that.
Turns out, they upgraded JSON.NET and it started to name fields in different case. Golang parser didn't really care about snake_case, camelCase or PascalCase but whatever parser we were using in Scala at the time actually did.
I think many of us are feeling these days that the rust standard library might already be too large and it's better to be very conservative adding stuff there or only after an extended period of time of independent vetting. I have yet to not experience that standard libraries turn into very stale and outdated things relatively quickly that carries an enormous maintenance burden.