Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think we should be taking a more “batteries included” approach to language and library design. The entire reason we’re in this mess is because we’ve decided it’s ok or maybe even preferable if stdlibs are rail thin, rendering base languages near-unusable.

I can very easily build a highly functional, pleasant to use Apple platform app with 5 or fewer top level dependencies. In many cases, I reach for between 0-2 total.

There’s no reason why this can’t be replicated elsewhere. The key is to make the programming language reasonably robust with at least 80% of common non-UI dev needs built in and put the remaining 20% and UI bits into a small family of well-supported, community-embraced, preferably first party libraries.

That would make it unnecessary to pull in foreign dependencies in the overwhelming majority of projects. What few do get pulled in becomes lightweight, easily verifiable syntactic sugar or libraries with purposes too niche to be worth targeting.

Of course this approach can go wrong too. You could easily end up with a monster like Boost, but that comes down to project administration keeping creep under control and proper modular design.



> I think we should be taking a more “batteries included” approach to language and library design. The entire reason we’re in this mess is because we’ve decided it’s ok or maybe even preferable if stdlibs are rail thin, rendering base languages near-unusable.

Nobody can agree on what those common things are in a general purpose langusge though. It works for something like Go, because it is largely used for servers and command line tools from what I can tell.

For Rust, well I would like to prioritise features for embedded development, someone else might want game dev features (geometry and physics stuff), yet another person wants data science things. And so on.

And even if did put some of those things in std, that is stable forever. That is how C++ ended up with an unusably slow regex implementation in the standard library. And it isn't fixable. Everyone just ignores it and uses a third party library like PCRE, RE2, etc instead (depending on their specific needs). I would not expect web or GUI things in the standard library for this reason, those standards change too frequently for that to be stable forever.

And even something like Python, which is known for data science and machine learning, doesn't have core libraries for those activities in the standard library. Numpy, Pandas, Scipy, Pytorch etc are all third party projects.


C++ ended up in that situation because they don't want to break backwards compatibility (ABI, API) or change contracts, except in some rare cases, even on major releases. Quite often, it is a vendor issue to break the ABI and fix issues, and some have refused to do that.

It's a self-inflicted issue that most other languages with "batteries included" don't have since they will document breakage and upgrade paths when anything changes, or they are not impacted as much since they are using an intermediate language as interface and avoid most ABI issues.

The implementation is fixable though, you can break the ABI and address your issues. A lot of standard library defects are fixed in an unstable ABI mode they have, but that requires you to link it statically or dynamically and then ensure all your shared object dependencies are using the same exact version. It's not trivial for everyone, but many do this (usually the static version).


Rust has committed to a stable API (but the ABI is explicitly unstable). Which makes sense for a systems language. It isn't (and doesn't want to be) a kitchen sink language.

But even something like Python has lots of cruft in its standard library. And they are willing to make breaking changes. Nobody should use urllib.request for example, requests is a far better HTTP client library. To the point where the stdlib docs tell you so.


I use urllib.request. It works fine, it’s always available, and I don’t need to push it through an audit.


> Nobody can agree on what those common things are in a general purpose langusge though.

Well, then take the Lazarus / Free Pascal's approach and just put everything :-P. It isn't like code takes THAT much space.

If you install Lazarus you get a ton of stuff out of the box, aside from the crossplatform WYSIWYG RAD IDE and desktop application framework you also get 2D graphics libraries (with a bunch of image i/o), networking libraries, (de)compression libraries, a webapp framework, database clients, parsers for json, xml, markdown, javascript and a bunch of other stuff (even a parser for the language itself), a TUI framework, bindings for a bunch of external libraries and other stuff.

And all that stuff (or at least most of it) work on all the platforms the compiler supports. Yes, using FPC you can probably (didn't try) make a pretty markdown viewer with inline JPG and PNG display support that runs on Windows 3.1 :-P.

Unfortunately some people are trying to break this and introduce online package management, but so far at least FPC and Lazarus comes out of the box with not just batteries included but an entire carton of batteries just in case :-P.


> Nobody can agree on what those common things are in a general purpose langusge though. It works for something like Go

It, as you put it, works for Go because its designers took a hard stance and decided that it was a language for network systems and you're on your own if you want to make it do other things. Nobody needed to agree because it was already chosen for you.

> For Rust, well I would like to prioritise features for embedded development, someone else might want game dev features (geometry and physics stuff), yet another person wants data science things. And so on.

But does Rust need to be all things to all people? The Go ecosystem has a separate standard library under the Tinygo project for those who want to prioritize embedded development. Even if there was convergence on Rust as a language, it doesn't technically need a singular standard library. There can be a distribution for embedded developers, a distribution for game developers, etc.


Maybe that’s a good place to make the cut. One Rust crate for game dev, one for data science, etc. Then their deps are pinned and monitored by the crate maintainers.


I wrote this on another thread recently, but my line for exclusion from standard library is a library that’s any one of:

- not obviously/generally useful (i.e. useless or too specific/should be a program not a library)

- obviously trivial (code it yourself when you need it/compose from other primitives)

- already available (open source) elsewhere by a credible team that supports and maintains it

Anything else… put it in the stdlib


Well I use python daily for the past 10 years and I never did data science. Data science? Everyone knows python is the language of cyber security.


This is spot on. I am extremely uncomfortable with the large number of transitive dependencies that end up in pretty much any non-trivial rust application. No amount of memory safety will save us if a tiny, ubiquitous library that nobody scrutinizes because it’s nested eight layers down the dependency graph gets compromised.

I think Go apps tend to have better dependency hygiene because the language has a better standard library, which results in better culture around dependencies.

I also think it’s frustrating that both cargo and npm totally ignore the decades of prior art from Linux distributions that have figured out good ways to improve dependency management. We’d be in better shape if there was a community curated subset of known-good dependencies that are release-managed together that the broader ecosystem could build on, sort of like Ubuntu having “main” and “universe”.


I learned programming from C# which has an excellent standard library. Just recently started using Go which seems to have a good standard library. Been using python for years, can't say I ever had a complaint about the standard library.

Rust meanwhile seems to be following some "no standard library" philosophy.

I feel like there's an opportunity out there to become like an amoeba: fund and build a third party Rust standard library, absorb absolutely everyone who is so grateful to have a library, and then get so big absorb Rust itself.


> Rust meanwhile seems to be following some "no standard library" philosophy.

As an embedded dev, I personally find it very useful to compile Rust programs without a standard library. I would not use C# or Python for an embedded project for the reasons you probably prefer them for your projects.


The guide that NASA put out for spaceflight systems was on HN earlier this year and one of the rules is "no heap allocations; all memory must be declared and fixed size for the life of the app." Isn't Rust the ideal language for that kind of limitation? We are talking C, C++, Rust, Zig as options I would guess; Rust certainly seems the ideal from those options.

But you gotta admit people trying to build GUI libraries and using linked lists don't fit.


I think one thing that has made Go have better dependency hygiene is not merely having "batteries included" in the standard lib, but the early focus on having production-ready implementations of a lot of stuff in the standard lib, as opposed to minimum viable implementations. I used to get a long way with one or two dependencies that would barely fan out at all. It's been a while since I worked on a Go project, so I'm not sure if it's still that way.


I think having production-grade implementations makes a huge difference also. Go’s net/http in the std lib feels like a good example of that, though I’m fairly new to go still. But it seems net/http still gets pretty big updates despite something like Chi being available as an external lib. Odin is doing this as well, even including raylib in the std lib. Odin is even more primed to keep you on the std lib as it doesn’t include a package manager at all.


So I went to have a look at Odin and was mind blown at the lispiness in the hellope example. A list of operators defined as "program"; that are applied to the accumulator then printed. It's a striking first impression for me and I want to see what the Odin people have cooked up.


Odin has been a masterpiece so far! Ginger Bill has a massive update coming in for it Jan 2027 as well. :) I think it's definitely going to take off in the next few years.


I wasn't when I saw they made the exact same stupid error as Python: if and switch are statements, so that you need a stupid ternary operator to do the same.


I think Go ends up with fewer dependencies mostly because there is more friction in finding them. With rust (and npm) you just "cargo search xxx", then "cargo add xxx". Go makes you web search and poke around github looking for something. It's not onerous or anything, but just that extra step slows things down just a bit. C projects tend to have the least because it's even more annoying to add them and you have to create build commands that deal with slightly different distributions (and learn makefile+pkg-config or cmake, or autoconf).

Certainly having a larger stdlib helps Go projects keep their deps down, but I don't think it's the real reason.


I don't really agree. You reach for a dependency when you need to do something non-trivial and the stdlib doesn't have what you need (or the stdlib implementation is bad). You don't just sit around running `cargo search` for fun.

I think the ease of adding dependencies does drive the idea of "microdependencies", where you have many dependencies that do one little thing, and then those dependencies likely have their own small dependencies.

Whereas in the C world, you'll probably have a smaller number of dependencies, but it's likely that some of them are very big dependencies that offer a lot of functionality (like glib, for example).

I could maybe see the argument that ease of adding dependencies discourages people from writing small things themselves. Good ol' left-pad comes to mind. In an ecosystem like C, I'd just write that algorithm myself, over and over and over, because pulling in a dep just for that feels annoying.


> Certainly having a larger stdlib helps Go projects keep their deps down, but I don't think it's the real reason

I disagree: having a big stdlib that encompasses a huge chunk of common functionality (logging, a plethora of network server/client protocols, etc) means that for basic needs, the question of 3rd party libs never arise, whereas for other language, the question is not if you need a dep, but which one. Making gorilla easier to find won't undo the cultural reluctance of adding a 3rd party dep when using the (very pragmatic, IMO) stdlib server is adequate to the task.


Actually I think this is more of a culture thing. Or maybe "is also" a culture thing.

One of the core tenets of early Go was the maxim "a little copying is better than a little dependency".

Probably because of this stance, they didn't even HAVE a dependency-management solution for years

I strongly agree the fewer dependencies the better, on average.


Perhaps, but I think it depends on where the Go devs are coming from. In my experience the lack of proper dependency management in older versions of Go didn't really lessen dependencies, it just made teams have to deal with annoying $GOSRC issues. But then I worked at a place where the devs used PHP (w/composer) and node before Go was introduced.

Personally I came from a C background so I tended to use deps more sparingly.

In the end it's a bit of a balancing act—lots of dependencies is a larger opportunity for these kind of supply chain attacks to affect you. Copying stuff into your repo protects you from that, but it also makes it way more likely that you'll miss security fixes, unless you're actively looking for them. Re-implementing what you need is also viable but it slows down the dev process.


I agree. Java has the same ability to quickly add deps, and in some case they can be sprawling, but it's still perfectly possible to develop complex apps without a lot of deps coming in.

Culture has a lot to do with it.


Does it? The only think I can think of not being in the standard library is a json parser


It absolutely is a culture thing. I've seen many threads asking about backend frameworks in Go, and every time there were lots of answers akin to "screw framework dependencies, stdlib is more than enough".


Go has an official package search at https://pkg.go.dev/

There’s no poking, unless you are into that sort of thing


> Go makes you web search and poke around github looking for something.

I'm not sure that's a bad thing. I suspect with increased friction, users are more likely to scrutinise dependencies before importing them.


Every Rust crate is heavily scrutinized these days using LLMs (which also caught this issue). The days when no one looked at dependencies are gone.


At upload time? Or by every client at download time? Or just adhoc by random users?

This does sound like an excellent way of improving automated package checks, even if it does result in some false positives and false negatives. For all sorts of packages, not just code dependencies.


By security scanners, the moment a crate is uploaded to crates.io. The way forward here is dependency cooldowns enabled by default for ordinary users (and crates.io changed to show the old version until the new one is a day or two old). Essentially, security scanners get to vet a package for a day or two before ordinary users see it in everyday development.


If those transitive dependencies are baked into the language runtime, how is anything materially different? You just shift the vector around.


Because the standard library of a programming language has a lot more eyes on it than one of the many small dependencies does.


You are just shifting where the eyes are, the amount of eyeballs (developer time) is the same. Unless the proposal is to increase the amount of (other) developers time.

The proposal ends up amounting to either shifting stuff around, or asking to other people to put in more effort? Into a project that usually doesn't pay (programming languages and runtimes)

Which by the way, asking other people to do stuff for free IS the attack vector, if you are the kind of dev or company that gets hit by these, your ultimate root cause is that your business strategy is using code without paying for it. No such thing as a free lunch, you will pay for it, among other things, with an increased cybersecurity risk.


There are more differences.

Packages can release updates arbitrarily, while standard libraries tend to have longer release cadences.

It's also more difficult to put arbitrary code into an stdlib, because stdlibs are scrutinized better.

Also, it's harder for some random anonymous developer to gain push access to stdlib repository.

One of the reasons so much Rust code is in libraries is not because there are no people to write it (duh), but because putting these in std commits maintainers to keeping backwards compatibility and slows down included package's release cycle.


Rust has a sizable and well featured standard library at this point. I think it would be absurd to claim that the base language is "near-unusable" if you are using it for system programming, which is its intended use case.

Huge standard libraries make sense for Java, Go, Apple platform etc. because they are developed by giant enterprises that can manage the overhead. Thinner modular systems are a more natural fit for open source development.


Thinner modular systems also require some good tooling and a good platform that ensures many security properties.

You will be trading a stable and vetted huge standard library for 200 small packages that may all be targets for supply-chain attack. Open-source projects don't all have the resources unfortunately to keep track of all their dependencies, so I'm not sure it's necessarily a more natural fit.


True. And the rust standard library is pretty good. But I really wish we had a good, fast, small futures executor in std. And accompanying async variants of File and Socket and so on.

Async rust is a jungle of weird compatibility questions. People treat async runtimes like sports teams. (I know I do). I wish it were more like Nodejs where async is just built in.


While admittedly async is a bit of a jungle for newcomers, I am glad that it is not built into the standard library. Where you see a bunch of needlessly competing runtimes, I see a healthy variety of different packages tailored to different users and technical specializations: tokio - reasonable default and server applications; smol - desktop applications or those who prefer the simplicity; embassy - embedded applications; futures crate - runtime agnostic utilities. I will gladly reach for any one of these depending on the kind of application I am developing.


If rust has a well featured standard library, why do the rust docs need to download 741 crates?


Serde is not even part of the standard library. There’s no HTTP client (let alone server), logging, decent date/time…

That’s not what I call well featured.

Most programs should need close to zero dependencies.

Of course they’re free to do what they want, and there’s the question of maintenance overhead[0], but while https://blessed.rs/ needs to exist, Rust’s standard library is deficient in my eyes.

[0] One idea would be to treat a language more like an Linux distro and have library maintainers like package maintainers but a unified quality, audit, and release process


It’s being used for a wide variety of other purposes however, and so perhaps it’s time to adapt to that reality.

While overhead is a real concern, I think it’s often blown out of proportion. Once a language achieves a certain baseline of stability and isn’t in constant flux and the standard library matures, changes become infrequent and maintenance load is low. The work is heavily front-loaded.


Not at all true, look at the large parts of Python's standard library that are effectively unusable or irrelevant. And they even allow some breaking changes over time (unlike Rust). This is especially true when it comes to anything related to internet protocols or file formats, but there are other modules that have far better replacements on PyPi too (re vs regex for example).


> It’s being used for a wide variety of other purposes however, and so perhaps it’s time to adapt to that reality.

Who is going to pay for the resources to make that happen, though? The reality is that the bulk of Rust contributions come from a fairly small group of people, many of whom are volunteers. Some are Mozilla employees, and they don't have all the funding in the world.

Yes, it would be great if every open source project could magically be able to meet the needs of all its users, but that just isn't how it works.


>There’s no reason why this can’t be replicated elsewhere. The key is to make the programming language reasonably robust with at least 80% of common non-UI dev needs built in and put the remaining 20% and UI bits into a small family of well-supported, community-embraced, preferably first party libraries.

The big reason is money & time. Maybe less so today if you are happy with an AI generated stdlib, but I don't think it's fair to compare Rust (built by Mozilla, who's primary product is an open source web browser) to Swift/AppKit (built by Apple, 4.6T marketcap at the time of writing).

When Go was released it had an amazing stdlib, but that was because Google was funding it.


Python is batteries included, but all the batteries have corroded.

Look at C++'s long in the tooth STL.

You don't want to marry a language to fast aging libraries you have to support for eternity. Better libraries always naturally emerge.

Rust's decision here is fine.

The only thing I'd like to see is the ability to programmatically limit transitive dependency count or depth in Cargo. I'd also like crates to specify whether they limit their own deps and whether they have panick-y behavior or not.


I think maybe the right answer is to have a standardized, curated group of libraries pegged at some sort of LTS release that only backports security fixes. However, someone would need to pay for creating and maintaining this -- and then you wonder where the money would come from?


someone tried doing this for rust, but the reaction was that it was vibe-coded/low quality.

https://github.com/rust-stdx/stdx

https://news.ycombinator.com/item?id=48571266

Note that there are baby versions of this that are uncontentious, for example

https://blessed.rs/crates

this is missing the LTS release. but it is a curated group of libraries that are relatively uncontentious to recommend.


That or potentially fast tracking merging popular libraries into the standard library. Or at least concepts from popular libraries. Basically all the "most downloaded" crates on the Crates.io front page should be candidates for merging into the standard library.

https://crates.io/


Good ideas are pulled into the standard library.

https://crates.io/crates/once_cell

https://doc.rust-lang.org/std/cell/struct.OnceCell.html

Not everything should be eligible for this treatment. Certainly not an HTTP library or something without extremely widespread applicability.

Nearly all of my projects used once_cell, so it's good to see that pulled in. I wouldn't want to see anyhow, thiserror, anything opinionated, or anything domain specific in the std. That's a weight you have to bear forever.

Remember how long Python 2 -> 3 took, and look at the ancient and awful stuff in Python 3's standard library. Rust is not the right language for this.


The fallacy is assuming that standard != unchangeable.

One can very simply....make breaking changes. Yes it will require some work to update but that's already the case when you upgrade library versions.

The dependency hell doesn't help anyone here, the downside is much worse - lots of bloat, overgeneric libraries and awful supplychain security.


I’m a big fan of how C# handles this. The standard library is versioned like any other library. Your program is explicit about the major version of the standard library you’re pulling in. And all major versions of the standard library continue to work.

For rust, this would mean something like adding std = 1.0.6 to your cargo.toml. The advantage is it means the standard library can deprecate and replace features. Upgrading std is an explicit step by the developer. Annoying, but in the age of LLMs it should be pretty easy.

The one big question I can’t answer is what happens if your dependencies use a different version of std? Are std v1 Strings compatible with std v2 strings? Oof.


> The one big question I can’t answer is what happens if your dependencies use a different version of std? Are std v1 Strings compatible with std v2 strings? Oof.

C++ went through that around C++11, where some types had to have ABI breaks (on some implementations, such as that of GCC's libstdc++). It has been a while, but as I remember it std::string was affected since it went from COW to SSO. Map might have been affected too (don't quite remember). It was a mess.

Rust can link multiple semver versions of the same dependency, but you can't pass data structures between those versions. For transitive dependencies that usually isn't a big deal if your direct dependencies use them internally rather than exposing the types in their own APIs. But std contain vocabulary types that everyone uses (Option, Result, String, etc) so that would really not work.


Rust already has this, it's called editions. The tricky part is it needs to stay ABI compatible to be able to link dependencies from other editions.


More than that. Rust requires that a program can be compiled from multiple crates, with those crates all targeting different editions. This limits a lot of breaking changes you might want to make. APIs can never really be removed.


Rust releases a new version every 6 weeks and the latest version is the only supported version. This is only tenable when the amount of breaking changes is tiny and each instance's impact is analyzed and cost-benefit tradeoff is checked and mitigations are put in where possible.

Some features, e.g. the never type[0], are held back years due to all the work that's needed to minimize the breakage.

[0] https://youtu.be/3jM4cnEVrLc?t=271


Can we pick and choose the good without the bad? If we agree stability is a net-negative we could just say there will be a list of blessed trustworthy libraries with extra scrutiny and bureaucracy (make no mistake this is completely necessary to what people are asking for) to make sure they don't go rogue. But without a guarantee they will stick around and get updates forever.


There's probably to truth to this, but I would weight the design intention behind the language just as or more heavily than available resources.

Swift was intended from the start to not only replace Objective-C and all of its use cases, but also take on new use cases and bring a number of features from other languages that were newer and had different dominant paradigms.

Certainly having a juggernaut like Apple behind it has been instrumental in its success in achieving those goals. That said, a community-lead project with similar aims could probably achieve those aims as well, given enough time.

I think it's just rare for enthusiast-led projects to set out to do such things. My theory on why things tend to go that way is that the types of people to start programming language projects tend to heavily lean nuts-and-bolts and theory with a purist/idealist sort of mentality that's more concerned ideological purity than practical usability.

So to sum this all up, a community-run practical, multi-purpose, batteries-included language likely needs to at least partially designed and helmed by product engineer types so it doesn't end up anemic and stuck in an ideological rut.


This was actually the main thing that put me off of Rust. I get the argument for a small std lib. I just also don’t agree it’s worth it. Go seems to handle having a batteries included standard lib just fine.


People confuse what they want.

They do not want a big stdlib. The downsides are real (the stdlib cannot make breaking changes), and there are no upsides (except, maybe, for faster compilation, since std comes precompiled).

They want more official crates (e.g. `regex` and `libc` are official crates, maintained by the Rust project). And the Rust project does not oppose to that, it just doesn't have the funding.


> there are no upsides (except, maybe, for faster compilation

Another big reason to put something in std is providing types for cross-crate compatibility. If I want to pass a String from one crate to another, I’m glad that string is defined in std so there’s an obvious type we can both use in our APIs.

C - for example - does not have this luxury. Everyone makes their own string type, and C APIs all need to translate strings at the api boundary. It’s super annoying.

It would be nice if we had a standard way in rust to declare a type as serialisable. Right now lots of crates have a serde feature flag to do this with serde. But (a) they need to put this behind a feature flag, since it adds a dependency on serde. And (b) this doesn’t work with other serialisation libraries.

The other big one is futures. There’s still no futures executor in std. Async crates have to decide if they want to tie themselves to a single executor (like Tokio) or be compatible. There is no async stream api in std. No async file api. And so on. It’s a compatibility nightmare.


> The other big one is futures. There’s still no futures executor in std. Async crates have to decide if they want to tie themselves to a single executor (like Tokio) or be compatible. There is no async stream api in std. No async file api. And so on. It’s a compatibility nightmare.

We need useful traits to abstract over async runtimes. But there are a lot of design points for runtimes so this can't ever go into std. There is embassy, for embedded systems. There is monio and glommio built around io-uring. Then there is smol intended to be simple and lightweight. And then there is the kitchensink of tokio.

Clearly embassy is the one we want in the standard library. /s (It is the only one I personally would have a use for. But no, I don't think any of them belong in std. Traits to abstract over them? Yes, absolutely.)

But even your other example of strings: there are many design points for strings too, that might be better fits for specific use cases: string builder (with a capacity, what String in rust is), copy on write, small string optimisation (like compact_str), even interned strings. So your example of a vocabulary type is somewhat flawed, a better example would be Option or Result. Even anyhow and thiserror build on top of those vocabulary types.


That is true, but this does not require a huge stdlib. And Rust is partially open to that (even adding async traits for IO in std is something they'll likely do in the future), but because std can't make breaking changes, you need to be very careful and slow.


Who said a standard library can't make breaking changes? That used to be a norm some time ago.

There are upsides, your program is smaller, better security because a random kid can't pwn your deps, quality and interoperability. What's not to love?


> Who said a standard library can't make breaking changes?

The std making a breaking change is the language making a breaking change. Most mainstream languages guarantee stability, certainly Rust.

> your program is smaller

How so? It doesn't matter if the code is in std or a crate.

> better security because a random kid can't pwn your deps, quality and interoperability

That's exactly what I said: you don't need bigger std, you just need more official crates.


>Most mainstream languages guarantee stability, certainly Rust.

That's their mistake, right there... When you're upgrading software, you presumably want a better version. You can't have something better without changing it. It's a logical contradiction.

>It doesn't matter if the code is in std or a crate. It very much does because you don't need 95% of the random stuff in the crates. Even for something like rand, you need an xorshift and that's roughly it. 25 lines of code, sorted. You can even write it as a copypaste "dependency" without much fuss in practically any language. That, versus importing a whole rand library with lots of different algorithms, deps on crypto, tests, OS random-based seeding, customisability, etc.

>you just need more official crates

I guess that's one solution but it would probably just be a better idea to split std in two SLAs, one is guaranteed for core stuff i.e. the status quo, one is YMMV.


> That's their mistake, right there...

No it's not. It's exactly what I want, at least.

> When you're upgrading software, you presumably want a better version.

Sure...

> You can't have something better without changing it. It's a logical contradiction.

No, that makes no sense. When I upgrade the rust compiler, I might want it to compile programs faster. I might want its borrow checker to be smarter so that it accepts more valid programs than it did before. I might want to have some new language features (like if-let chaining or GATs).

What I absolutely do not want is to have to change the code in my programs so that it can be successfully compiled by the new version of the compiler.

Yes, certainly there are some times in the lifecycle of a piece of software that there's an improvement that requires a breaking change, but you believe is important and necessary, so you bite the bullet and do it. But you burn user goodwill every time you do that (maybe not your goodwill, but most people are not like you in this regard), and create work for people that they would probably prefer not to do. You also create fragmentation, because people aren't going to upgrade their new stuff to the new version all at the same time, and some people may never upgrade. All of this has costs, and deciding never to make breaking changes (or at least keep them to the absolute bare minimum) is a reasonable choice in the face of those costs.

Maybe it's not the choice you personally would have made, but it doesn't mean it's a mistake. They just value a different set of things than you do, in a completely reasonable way.


Yeah it's pretty wild to demand things to be added to the standard library and then tell you that breaking backwards compatibility is fine. The first demand caused the need for the second. Meanwhile for non std libraries you can go ahead and do whatever you want.


> You can't have something better without changing it. It's a logical contradiction.

Sorry, that's a strawman. Of course you cannot have a better version without changing things. You definitely can have a better version without changing public API.

And people upgrade their toolchain not because they want a different API. They mostly want bugs fixed, support, and sometimes features. Breaking the API has a huge cost and minor benefit.

> It very much does because you don't need 95% of the random stuff in the crates.

But why does it matter if all that stuff comes in std or in an different crate? As I said security doesn't play a role here. You can't even avoid compile-time cost because the std can only come precompiled because it only changes with the toolchain, which is another downside we want to avoid.

> I guess that's one solution but it would probably just be a better idea to split std in two SLAs, one is guaranteed for core stuff i.e. the status quo, one is YMMV.

And again, how is that different from what I say, other than calling "official crates" as "std"?


> Who said a standard library can't make breaking changes?

I don't want my language creating work for me. "Batteries included" is a feature of higher level languages. Rust is going to live for a very long time, probably beyond all of our lifetimes, and it needs to endure fads without picking up baggage and liabilities.

Rust could have a mandate where certain crates are "blessed" as recommendations. This could come with additional review, oversight, support, documentation, etc.

The Cargo.toml spec should have the ability to limit direct and transitive dependency count and depth. (As well as other things, like "nopanic" annotations/guarantees.)

The Crates.io repo should grow namespaces so we don't typo squat popular packages. It's much harder to miss organization names changing than package names changing.


This is not true. I want a big standard library and I am more than willing to deal with the downsides just like i have with python.

Batteries included ia the only sane way to do programming languages.


You haven't provided any reason why you want a batteries-included std, while I did include a rebuttal of reasons people commonly claim.


On the other hand, there are some bad Go standard libraries that are frozen in time.


Yeah, but I will take a not great library that works everywhere the compiler does, than be at the whims of which platforms are supported by 3rd party libraries.

I can use most of the clunky Python, Java, .NET and if it must be, Go, standard libraries, than hunting down for dependencies with platform tier support and such.


Which is how you get the mess that is the standard library of C++. Where regex is an unusably slow joke and everyone uses third party libraries for that instead.

And tons of parts of the standard library (and language) being cordoned off as "legacy, don't use for new development". Of course figuring out what you shouldn't use in C++ can be hard too. It isn't well documented (or universally agreed upon), and it takes having it as a full time job to be able to keep up with these days.

No, I much prefer the way Rust is doing it.


Regex drama is overblown, it is quite usable for most business purposes.

Sure, it would be great if the volunteers that contribute to C++ compilers, would improve regex instead of adding the gazillion of features that each ISO C++ standard requires, mostly on their free time, because devs can't be bother to pay for compilers.

Additionally the companies that actually sponsor those compilers also have bigger priorities than improving regex implementation.

Finally if you want to stick regex into a micro-benchmarks context, there are plenty of options out there, as you point out.

The Rust way, like you can have any async runtime, as long as it is Tokio?


Regex issues aren't at all overblown, at least in the fields I have worked in or in fields of people I have talked to.

As for async in Rust, I wrote in another comment that yes that is an issue. What we need is std to define traits that all the different runtimes can implement. Because there are absolutely reasons to use alternative runtimes, depending on what you do. I mostly use embassy (embedded microcontroller development) for example. And if you want thread-per-core with io-uring you want glommio or monio. Smol is simple and compact.

I could also see uses for async in desktop GUI or gamedev (but to my knowledge there isn't any runtime suitable for those domains yet).


Platform support is a non-issue for every library being discussed. This is an array construction macro, it has nothing to do with what operating system you're running on.


Just because some trees have a specific trait, it doesn't mean the whole forest is the same.


Your argument only works for no_std crates, but the std exists and allows the vast majority of crates to be cross platform out of the box.


I bet there are plenty of them that don't work on IBM i, while Java does fine there.

Just to quote a possible scenario among others.


Add more and more to a standard library, and you're going to start losing your "works everywhere".


Naturally there is a balance, still I think Java, .NET, Python, Go, Smalltalk, manage quite well, while having subsets to slim down when needed for specific deployment scenarios like embedded devices.


It's interesting that you include Java in there, as I've worked on some pretty bog-standard Java code bases where the dependency tree didn't look all that different from a cargo project's.


It starts by those that reach out for Log4J, when java.util.logging.Logger already does the job.

Guava for whatever cool reason, and so on.


which, as painful as it may be, is ok. Better to have a safe functional stdlib library than a exposed external crate (which then asks the question; what's the replacement...)


Those aren't the only two choices.

We also have the classic example of PHP with numerous not safe stdlib ways to use mysql.

To me the answer is still to vendor your dependencies and don't be on the bleeding edge of updates unless you're willing to invest the time into validating them.


> We also have the classic example of PHP with numerous not safe stdlib ways to use mysql.

I think it goes without saying that emulating PHP is rarely a good decision.


Don't forget C++ where large parts of the standard library are unusable (regex is slow and unfixable) or soft deprecated (dont use iostreams for formatting, use std::format, etc).

No, I prefer what rust is doing. It suits a system programming language. Which is what Rust is.


Regex does the job for most business software, iostreams is alright, using them since 1993, std::format is cool provided one has control over their compiler version,...


Regex is absolutely not suitable for most use cases that I have come across. Iostreams formatting is awful, especially if you care about internationalization (which is very common).

Not sure what the issue with std::format would be here, you would have to elaborate. Obviously you need to specify a minimum version to have support for it at all.


It works good enough for stuff I would be using Java or .NET for.

Not every application requires internationalisation, especially server code or internal company tools.


Which isn't actually a problem. You can ignore the bad standard library and use something different.


> I can very easily build a highly functional, pleasant to use Apple platform app with 5 or fewer top level dependencies. In many cases, I reach for between 0-2 total.

> There’s no reason why this can’t be replicated elsewhere. The key is to make the programming language reasonably robust with at least 80% of common non-UI dev needs built in and put the remaining 20% and UI bits into a small family of well-supported, community-embraced, preferably first party libraries.

I wholeheartedly agree. Most standard libraries out there (when I say most I easily mean of 99% languages that I know) have pitifully tiny implementations. They at most provide for string parsing, io, a bare networking layer (that's at best a wrapper around the POSIX syscalls), threading and that's it. If you want to do anything more complex, you either have to roll it yourself or use an assortment of the "blessed libs" for any given language, that may or may not work properly, and may or may not even work well with each other. I got pissed off by this at some point so I decided to go ahead and roll my own standard lib that would actually have everything I need to.. actually develop apps. Now I know most people probably aren't going to be using it (mainly because its functional API isn't what most C++ devs want) but for me it's actually been brilliant.


> we should be taking a more “batteries included” approach to language and library design.

Okay, but how do we decide which approach to a problem should be standardised?

Rust's approach of "provide a minimal core to build libraries on top of" provides a fairly significant benefit - flexibility.

A prominent example is async: Rust has three fairly prominent async runtimes (being Tokio for general server-side use, smol for a simple non-work-stealing approach and embassy for embedded). Which one do you make the default? Tokio is very complex and work-stealing is arguably a very bad default in terms of development complexity. smol doesn't fit some workloads because of a simpler execution model. Embassy is way lower level and much more constrained than most applications need. Standardising over one implementation instead of the current (even if still messy due to early portability woes) standardisation around traits and general async semantics would make switching runtimes more problematic, even though the general case is solved with the same approach as Python's

Database access is also a complicated topic. Rust has multiple fairly popular DB libraries. The first two that come to mind are SQLx and Diesel. Both are too complex to be a part of the standard library, both required a fair bit of breaking changes, and if you wanted a raw core to build on top of - you can't really build SQLx on the type of core e.g. Go's database/sql has, you'd be basically forced to implement it all from scratch to handle the compile time type checks anyway

RNG sounds simple, except there are good reasons behind both rand and fastrand existing, and behind rand not being stabilised at 1.0.

DateTime handling is notoriously complicated, has multiple competing implementations (chrono, time, jiff) with various trade offs

Where do you draw the line? What do you standardise and which implementation do you standardise against?

I've seen golang exemplified a lot in adjacent threads, but golang's standard library both has fairly significant issues (json, no set type, very limited CLI flag parsing, UUID took until 1.28) and is standardised around a relatively specific goal (unix server usage)

Dunno, I'm not entirely sold on the whole "extend the standard library" idea for a language meant to run both high level applications and embedded stuff. If anything, recommending blessed.rs more is a better way IMO


> I can very easily build a highly functional, pleasant to use Apple platform app with 5 or fewer top level dependencies. In many cases, I reach for between 0-2 total.

> There’s no reason why this can’t be replicated elsewhere.

It is replicated elsewhere and is not limited to Apple; I can do the same using a tech stack of perhaps 2x techs and very limited (manually added) deps. I'm thinking Lazarus, which comes with most things I'd need for most apps.


Ok in theory, but I don't think it applies here. Maybe it does in a cultural way.

Arrayref seems somewhat niche, but its kind of in the standard library as of Jan 2026 as std::slice::as_array https://doc.rust-lang.org/std/primitive.slice.html#method.as...


For the example given in the arrayref docs, I'd say you should be using std::slice::as_chunks, stabilized in 1.88: https://doc.rust-lang.org/std/primitive.slice.html#method.as...


You can never please everyone , and some stdlibs can age like milk (see ocaml).

Having libraries allow the community to explore rather than saddling it with a bad default. See the number of http server frameworks etc.

The real solution here is not for the stdlib to become a black hole, swallowing up every half-way popular library a but rather for “rustaceans” to adopt a more security-conscious approach to writing programs- C programs typically have few and well-chosen dependencies. Some languages, like Odin, completely eschew package managers because they see the very real issues stemming from writing a program with scores or hundreds of dependencies.


You seem to be ignoring that most people who want off the infernal npm/cargo hamster wheel are conscious that it implies a bit of spoilt milk or moldy bread in exchange for stability and that they're fine with it.

The gains are just too numerous and important to reject the few rust spots on a "good enough" stdlib.

But well, the kind of people who nod a bit too much while reading https://boringtechnology.club/ aren't using Rust. They're on C99, ANSI CL, Ada, maybe Python, Perl or Tcl.


I don't see the problem with boost? Isn't that a perfect example of your well supported, community embraced option? I certainly feel much safer pulling something in from boost via the official debian repos than I do pulling a random package with npm or cargo or etc.

Primarily I think the underlying concern has to do with the pathway for authoring code. When you have contributors whose submissions are gated with a rigid third party process and where that third party is the one responsible for curating the code (as opposed to the author also being the curator as well as the publisher) then you have the possibility to catch a lot of wrongdoing before it succeeds.


> I don't see the problem with boost?

There are many issues that I've seen taken up with Boost, but my personal peeve is how it can make building projects that incorporate it a pain, both because of its sheer size but also because it can sometimes be difficult to appease with its own dependencies.


that seems unrelated to the security problem to me


The Odin programming language does this! It has also decided not to provide a package manager.


Go took the same approach and ended up having to implement a halfarsed one when everyone started implementing their own.


Has Go not had the most secure ecosystem?

What is your critique of their approach? Is it not the case that `go get` is the only one which doesn't even provide a way for the person downloading to run the downloaded code until it is actually executed by the consuming codebase? That seems pretty sound by comparison.

I guess you're saying, "they had to" meaning they should've seen the need and provided it? I'll say this to that (imagined) take; plenty of useful software was made without it, and they got the job done when they knew the absolute most about what a good solution would need. I totally understand being annoyed at the fact that this is the story of every evolution in Go, but I genuinely think they're picking good implementations when they decide on them.


> What is your critique of their approach?

It’s half arsed, brittle and far from user friendly.

> Is it not the case that `go get` is the only one which doesn't even provide a way for the person downloading to run the downloaded code until it is actually executed by the consuming codebase?

If you’ve added the package to your imported then odds are your next step is going to build it. Thus negating any benefit.

I think the real issue is malicious packages entering package ecosystems. Whether your package manage executes on downloads or not is moot because you’ve still got untrusted code sat in your project imports, just waiting to be accidentally executed.

> I guess you're saying, "they had to" meaning they should've seen the need and provided it? I'll say this to that (imagined) take; plenty of useful software was made without it, and they got the job done when they knew the absolute most about what a good solution would need. I totally understand being annoyed at the fact that this is the story of every evolution in Go.

I think you’re being too charitable here. I think Russ Cox just didn’t want a package manager because C doesn’t have one. But ended up relenting after everyone nagged the Go team for years afterwards.

And really what they built was the bare minimum to manage package version pinning and updates. But it has none of the visibility that central package repositories have. So how do you know if a Go package has been compromised when the only source of truth is the compromised origin?


Ultimately I think devs need to think about their dependencies and decide which ones get pinned and a serious review before pulling. If the thing has got binaries, it gets a serious review. If it does anything with cryptography, it gets a serious review... etc.

I don't think automatic updating is a good idea at all. It's just the honor-system, and trust is a security flaw.


Exploits can be shipped in any type of dependency. Remember the compression exploit that affected even SSH?

The problem is your solution depends on the honor-system. Ie “trust me, because I’m just a YAML mashaller. Why would I want to inject a crypto miner?”

That’s why package databases exist. They are meant to be centralised databases of peer reviewed and CVE checked resources. But Gos approach pushes all that responsibility onto each and every developer.

One thing it does get right, in my opinion, is removing the value of name squatting.


It's interesting how this plays out with C/C++. There's not really a package manager, and so the host system has to have vetted packages. It moves the burden on to the system maintainer.


Yup. It’s a system that worked when it was relatively safe to assume a chain of trust. But it’s not scaling to the era of AI agents writing patches, nor the increasingly number of attacks against existing foundational packages.

We really do need to rethink the security model behind open source.


Thanks for the thorough reply. I personally haven't experienced brittleness/unfriendliness, but I have only written around 10k lines of Go. Not exactly a power user, but I like to think I understand it.

> If you’ve added the package to your imported then odds are your next step is going to build it. Thus negating any benefit.

I actually think this is the benefit! I don't need to go to a website to see what changed, I can just have a look at the code. The best dependencies have a changelog. Ideally I can look at a diff.

> I think you’re being too charitable here. I think Russ Cox just didn’t want a package manager because C doesn’t have one. But ended up relenting after everyone nagged the Go team for years afterwards.

That's entirely possible.

> So how do you know if a Go package has been compromised when the only source of truth is the compromised origin?

Fair point, and probably worse in this future we're in now that NIST is drowning in CVEs and has turned away from some share of them.


Spoiler alert! JavaScript has no language provided package manager.

If Odin gets moderately successful someone will probably reinvent it.


NPM came along in 2010, Javascript was huge before that.

It was only when people wanted a common approach to shipping both in browser and "native" that this glitch happened. I believe a standard library and "batteries" would have abated it entirely or resulted in a slightly less-bad situation. I do think Cargo is a slightly less-bad situation in many ways, and that it can be done better.


> It was only when people wanted a common approach to shipping both in browser and "native" that this glitch happened.

Nah. Npm was invented because node had its node_modules directory. But it was tricky to find and download modules you wanted to use. Until npm, you had to add libraries to node_modules by hand. And check them in to git or something. And keep them up to date somehow. Npm added a searchable index and a tool to automatically install all your modules. Npm was only bundled alongside Nodejs many years later.

Bundling was separate. I can’t remember if browserify predated npm or not. But it was a wild idea at the time to make node modules build for the browser too. Browserify - and later webpack and friends - work with or without npm.


Sure. Maybe threshold is bit higher than moderately. But unless your language tries to sabotage itself by making code artifacts uncomposable (a la C/C++ where best way to compose libraries is through shell commands) some package manager will be inevitable.

Batteries also don't help if dependencies don't replace them. Arrayref functionality has been part of Rust std lib for a while now.


> I think we should be taking a more “batteries included” approach to language and library design. The entire reason we’re in this mess is because we’ve decided it’s ok or maybe even preferable if stdlibs are rail thin, rendering base languages near-unusable.

Rust is one of the better languages when it comes to batteries included in it's stdlib. Even then, it's impossible to have a 100% coverage batteries included language because nearly everyone on planet earth has some different/unique use case for their code that doesn't fit a stdlib.


I really like the approach Julia is taking. The std libs are their own packages with their own versions. You can then build your own “standard library” by compiling a sysimage that freezes the versions (you can add third party packages as well).

Practically speaking the standard library is just meant as “what’s included in the default sysimage”.

This allows to have different standard libs for different use cases. Think of Linux distributions in a sense. There are already a couple of alternative “stdlibs” out there for different focuses.


A big selling point of Java also was a very strong built-in library, e.g. with red-black trees (TreeMap) and concurrent lock-free zero-mutex queues (ConcurrentLinkedQueue).

No wonder competitive coding champions sometimes preferred Java as they don't had to spend time coding it from scratch.


>I think we should be taking a more “batteries included” approach

https://www.youtube.com/watch?v=GZOuz-SG7-g

Funny how you skipped "I should build my own batteries" and when straight to "increasing and centralizing the duties of your main gratis 'vendor'".


I don't like the "bring your own" approach because unless one happens to be an absolute tour de force 10x engineer unstoppable god of a programmer (which most of us, myself included, are not), whatever you build is never going to be as well-rounded, fleshed out, and complete as something built by a larger organization, especially when it comes to UI libraries (which are monstrous projects if done right, e.g. meeting accessibility requirements).

I'm happy to contribute to a larger effort but anything I can build on my own is going to be a thin, flimsy happy meal toy compared to something with the backing of a company or well organized FOSS project.


The counterpoint is that whatever std invents is probably not going to be as good as what moviated people in the community make.

The rand crate is my favorite example of this. They have a whole bunch of different rngs. All rated by quality and performance. Some are csrngs and some aren’t. It’s a delight. “Batteries included” languages don’t come close.

Then there’s serde - which uses a clever technique with traits to allow compile time specialisation and optimisation of binary and json serialisers. Most people had no idea that was even possible in rust until someone in the community made it.

Maybe std should pick up the best crates from the community and bring them in house? This might be a good idea. But also maybe not. Since serde came out, several other crates have found ways to crush its performance numbers. Often by 2-5x if memory serves. What a relief we didn’t immortalise the slow version of serde, right?

I’m playing devils advocate here. I think std should be a bit bigger too. But there are real tradeoffs in doing so.


> Maybe std should pick up the best crates from the community and bring them in house? This might be a good idea. But also maybe not. Since serde came out, several other crates have found ways to crush its performance numbers. Often by 2-5x if memory serves. What a relief we didn’t immortalise the slow version of serde, right?

I think periodically rolling in the best crates is the way.

Don't immortalize anything, though. Always reserve the freedom to swap out integrated crates when something better rolls around. Negative impact can be reduced by limiting these kinds of changes to major new versions.

Another option is to swap internals while keeping the same public API. Apple has done this several times with Swift, resulting in massive speed improvements in things like string handling and JSON decoding without breaking anybody's code.


> Don't immortalize anything, though. Always reserve the freedom to swap out integrated crates when something better rolls around.

That's a non-starter, though. Once it's in std, it's not an "integrated crate", it's in std, just like any other part of it. Removing it is a breaking change, and I don't want a new incompatible Rust major version every year or two (or even five or ten, honestly). (Of course, Rust has editions to make breaking changes possible without affecting old code, but there are limits to what you can change in editions.)

Pretty much every language ecosystem fights really hard to maintain backward compatibility as they evolve, and every time they fail to do that, it causes headaches, sometimes for years. Python 2->3 is of course the canonical painful example of that, but there are others that are instructive if you only take the time to research.

(And before you mention something like Zig, which changes incompatibly frequently enough, remember that the author has made an active decision to remain pre-1.0 unstable. That's one reason why the Zig community isn't that large. That's not me looking down on them; that's an active choice they've made, and they're free to make that choice. But bigger/popular languages like Rust can't go back in time and decide to be like that.)


I don’t quite get this mentality.

Yes, it’s annoying to have to make fixes around my codebases periodically, but to me that’s a hell of a lot less annoying than having to pull in a mile long list of dependencies to do anything of consequence. It’s also better than the bad parts of the language being frozen in time forever.

Apple breaks things with Swift not constantly but on occasion and while there’s some griping around it, it’s not a big deal. We make the needed changes and move on, knowing it’s enabling improvements that will be nice to have.


I agree that building your own is hard and something that can be done by exceptional engineers, but software is famously a winner-take-all industry, even if the optimal strategy for lower percentile programmers and median programmers to import generic modules, as long as building your own results in the best product (which it does, whether importing frontend or backend modules, if you hand code something, it's going to be the optimal strategy for building a winner product. Even programmers that are median or below average probably have an incentive to aim for being exceptional, because the best value of median and below average programmers is not building median or low value software, but having a shot at building exceptional software.

Also the challenge of building a generic module is much larger than building your own. A generic module needs to have flexibility for many different options and integrations, when you build your own you build just what you need and tightly integrate it with the product


> Also the challenge of building a generic module is much larger than building your own. A generic module needs to have flexibility for many different options and integrations, when you build your own you build just what you need and tightly integrate it with the product

True but I believe overestimated. Usually rather than building what's needed, what gets built is what is thought to be needed (often a substantially smaller subset), and then over time you end up building a markedly poor version of a generic module.


I think that even for core modules like an http server or client, but that's not the case and we don't need to get into that debate.

let's look at the actual package from OP

>https://docs.rs/arrayref/latest/arrayref/

>This package contains just four macros, which enable the creation of array references to portions of arrays or slices (or things that can be sliced).

I'm no rust programmer, but that doesn't sound like something that moves the needle. I talked about the ratio between value to risk being a relevant decision parameter, so getting infected by an http framework would be defensible, getting infected by adding this to a project to me is a PIP, and getting infected by installing leftpad or a custom cursor plugin in an IDE would be fireable.


You seem to be under the mistaken impression that the Rust team is some huge organization. They're not. They are certainly larger than you or me sitting behind our keyboards at home, but they don't have endless resources, and they are always stretched pretty thin.

So sure, you want, say, a JSON library in Rust std? Who's going to shepherd it in and then maintain it? Well, you could hope that the people behind serde_json might want to do it, but what if they don't? Where are you going to get the people with the time to do it?

For something like Go, the answer is easy: tap into the money printer that is Google, and hire more people. But for Rust? Hell, the core team has probably only declined in number over the years (Mozilla layoffs, for one thing).


There's also the time factor. Something you build on your own will not have decades of development and polish behind it.


>https://docs.rs/arrayref/latest/arrayref/

>This package contains just four macros, which enable the creation of array references to portions of arrays or slices (or things that can be sliced).

The package in the OP is definitely not in the category of decades of development, it's closer to a leftpadism.


> I can very easily build a highly functional, pleasant to use Apple platform app with 5 or fewer top level dependencies. In many cases, I reach for between 0-2 total.

Most of the things I wrote in Rust have less than 5 dependencies. Those 5 dependencies then depend on 20 other dependencies each. Those 20 dependencies have 2-3 other dependencies. And so on…


Counting the number of crates in the dependency graph in order to gauge exposure is a misunderstanding of how Rust works. In Rust the crate is the unit of compilation. Unlike in Javascript, the fact that a crate has dependencies on other crates does not mean the author pulled in random code written by someone else, it often just means that the author wanted to leverage crate-level compilation parallelism by splitting a large compilation unit out into several smaller ones.


with LLMs it's becoming more common to just vibe up anything you need that might be missing. even if there is an available package you can pull in. the most secure option as well


> There’s no reason why this can’t be replicated elsewhere.

There are plenty reasons. Just not technical reasons.

At the end of the day, people like to feel useful. The ecosystem that attracted communities are those ones where everyone can feel they're contributing back to the communities. That's why the most successful languages, save those backed by big companies, are filled with very small libraries.

No body gets excited making C#/nuget packages.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: