Or am I spoiled by .net, which has an excellent bcl?
Or am I spoiled by .net, which has an excellent bcl?
- WinForms, WPF, UWP, WinUI 3.0/WinAppSDK, MAUI
- WebForms, MVC Framework style, MVC Core style, Razor Pages, Blazor, Hybrid Blazor (tryig to fit everywhere there is a Webwidget from the previous line)
- Latest design decisions being driven by Azure and ASP.NET requirements
Stuff like minimal APIs driving C# features, e.g. lambda improvements required for replacing controllers with minimal APIs.
There are hardly any desktop or COM improvements that are taken as seriously as the way the ASP.NET is.
EF Core works best with Microsoft offerings, SQL Server, CosmoDB,....
The whole aspect oriented framework now in preview to enable AOT for ASP.NET workloads.
Again: it is not a part of the language or runtime. If you do, however, have links to code changes that prove otherwise - please do post them.
Meanwhile, COM interests only a limited part of community, and there also exists actively maintained https://github.com/microsoft/CsWin32
A popular (if not the most, depending on location) choice to use EF Core with is PostgreSQL.
Meanwhile, ASP.NET Core simply continues to evolve just like other libraries, whether they are a part of dotnet/* or not.
GUI continues to get better with development of AvaloniaUI, Uno and MAUI.
.NET as a whole is way bigger than some of it's "first-party" solutions that you don't even have to use, that have little impact on language, runtime and compiler themselves aside from "this is a good demonstration where we can improve the general case". There also exists Unity, many companies using completely non-standard stacks on top of the language, etc. But you deliberately choose to ignore this information for unknown reasons.
It is genuinely bewildering to see this kind of flawed and strange impression of the ecosystem not rooted in reality. I suppose addressing this involves reading documentation and source material, which perhaps proves too challenging.
The difference between us, is that my professional existence is not tied to praise .NET folks for everything they do.
Everything that Microsoft ships on Visual Studio is part of .NET, without yes and buts.
"multiple incompatible ways to do async" is another way of saying "you can use async on a tiny microcontroller without any OS at all, or in userspace on a 128-core Epyc CPU, or in the Linux kernel"
That's a unique feature of Rust compared to any of the languages with a single executor baked into the language like C# or Javascript. It does however come with costs, and one of those costs is that people who just want to write a web app are exposed to details they would much rather not have to see.
Practically speaking, however, you are right.
MS has massive resources and can align the many many modules of .NET into major releases. The Rust project does not.
My knowledge about Rust's library system isn't great so there are probably several gaps in my argument.
There are crate "recommendation lists" like this: https://blessed.rs/crates
While these help, Rust's small standard library has slowed Rust adoption where I work because our IT security team must approve all third-party packages before we can use them. (Who blindly downloads code from the Internet and incorporates it in their software?)
This means that unless there is a compelling need to use Rust, most teams will use C# because .NET is approved and has almost everything. I prefer Rust to C#, but we are not using it that widely yet.
It's not a fair comparison, but it is how things currently are.
Do they also review the original tooling? Why would one single out third-party packages?
Everything used for software development is explicitly approved. This includes programming languages, compilers, debuggers, IDEs, etc. We are primarily a Microsoft shop, so the majority of our development tools follow that direction.
For FLOSS libraries, the approval process covers both IT security review, a source code scan / static analysis, as well as a legal review of the package's license to ensure it is not on the prohibited list.
This makes management happy since it prevents potential security and legal issues. It keeps our customers happy since they get quality software made from fully traceable components.
When a CVE is announced, we know immediately if we are impacted and what will need to be fixed.
Some places have no idea what their dependencies are. I am sure there are lots of log4j horror stories from Java shops that were not so careful.
There is no magic trick, just people (both hired and community contributors) who invest their experience, skill and ambition to make great technology.
I disagree.
It's more like the code has ascended (https://nethackwiki.com/wiki/Ascension). It has won because it is useful enough and mature enough to accompany the language and serve everywhere.
Depending on what it does, standard library code may be used everywhere. For example, would you say that the Rust's Vec (https://doc.rust-lang.org/std/vec/struct.Vec.html) is "dead"?
It can still get new features added. It will still get bug fixes. But its primary APIs are engraved in stone, never to change because it would break all the code that depends on it.
Granted, some code that goes into standard libraries will eventually fall out of use, or will be replaced by something better. In such cases, the code should be marked deprecated and eventually removed in a future edition of the language. In that case, it is like the code retires or could even be forked into a community-supported library in case it does still need to be used.
Adding more stuff to the interface is ok in terms of things continuing to work and over time makes the library rather large. If the new interface is better there's a good chance it's different and thus inconsistent with the previous code.
Bug fixes are tricky too. If they're semantically observable people probably depend on the current behaviour.
Languages vary in how well they handle this. Python and C++ are the usual mistakes are forever example. Perhaps rust or c# have a better strategy.
Training users to act on deprecation warnings and ensuring they actually have an alternative would be a good play. Lua manages to break things a bit over time. Python tried it once and will forever fear the flame. C++ refuses to recompile anything and fears the word ABI.
It's happening in Python 3.13: https://peps.python.org/pep-0594/
Hyrum's Law (https://www.hyrumslaw.com/) definitely does come into play, but bugs do need to be fixed. Good communication is essential so that library users can adapt. In extreme cases, jarring fixes can turn into new APIs.
>> Languages vary in how well they handle this. Python and C++ are the usual mistakes are forever example. Perhaps rust or c# have a better strategy.
Rust's Editions (https://doc.rust-lang.org/edition-guide/editions/) offer a nice way to gently break backward compatibility in order to keep everything progressing.
For Rust it can't happen, because of the backwards compatibility guarantees: you can mark an API as deprecated, and potentially make those unavailable in later editions, but code that compiled on 1.0 (2015 edition) should continue to compile in perpetuity, so the API has to stay around in the stdlib so that older code still works (we dont have an stdlib per edition, it wasn't even originally envisioned to have editions affect the stdlib, only syntax and semantics). It shouldn't be a problem, but it is an inconvenient burden.
The biggest problem is the exploding transitive dependency graph, including multiple versions of the same basic utilities.
Even small projects can suffer from this. It's too easy to reach into the grab-bag at crates.io and pull a little utility out and use it. I've personally built very small things only to find multiple versions or implementations of e.g. sha256 or rand or whatever in my own dep graph, and it gets really hard to prune them out.
At work we're fairly diligent about this but still it's exploding. And when you consider the matrix of minimum-supported toolchain versions for different projects, and if you're stuck on an older Rust toolchain, it's even more pain.
Rust projects starting from scratch could consider vendoring their dependencies rather than pulling from crates.io. But support for vendored dependency in Cargo is weak/bad. You run into all sorts of nightmares especially when using workspaces.
It's heresy and I'll get downvoted for it, but: More and more I lean towards using bazel or gn/ninja for fresh Rust projects, and walk away from cargo & crates entirely.
Cargo (+ Crates.io) was delicious to me at first, but it's a meal that tastes worse the more I have of it.
EDIT: it strikes me that I didn't say why exploding dependencies bugs me so much. There are logistical reasons for sure (competing APIs, compile fails, binary bloat, etc.) but the primary reason is: exploding complexity. Every additional dependency is now a potential problem for future me/future team.
This is the first step of your journey.
Next step is realizing the same about the language itself.
However, I also think the vast majority of Rust projects out there right now are using the wrong tool. They're using Rust to write web services and web sites, and the developers are coming from places like NodeJS, TypeScript, etc. It'd be fine, but it's I believe pushing the emphasis in the Rust language community in that direction.
I worked for years in the relatively sane subset of C++ that we used at Google. It's fine, I have to do C++ here and there for work, and I could even go back to it if I had to. But I mostly like Rust, and would miss it. Zig made some decent choices, but others I don't necessarily agree with, but I'd consider it for embedded or low-level dev of various types
Of course they are, same as what had happened with (to) C++. And the people that use Rust (or C++) because it's "fast" are mostly the same that write code which would be faster if it would have been written in Go, Java (Kotlin?), or C#. I guess every generation of programmers must make the same errors.
I prefer not to consider other people as ignorant or stupid. They just have different motivations or reasons.
But back to the topic: I do think Cargo should have started from looking at Maven as an engineering foundation rather than NPM. Maven shipped from the start with solutions to many of the problems that Cargo is sick with because they were not considered.
Oh, I didn't want to imply people are stupid, after all, I (and many other people) made the same error 25 years ago.
But talking about Cargo (the last time I used Java there had been no Maven yet), the packages really need at least some sort of namespaces. But I don't think that (newer) languages without a really "battery" standard library can evade the problems of "too many transitive dependencies".
And don't get me started on the async story. Yikes. Rust is the language that Tokio ate.
In the spirit of being charitable, I'd like to hear more about your journey here.
At the same time, to some people the comment above might come across as snide and/or unjustified. I can't get a confident read on the author's intended tone.
I would suggest Buck2 (which is written in Rust, so you can use the existing tooling to build it) https://buck2.build/ https://github.com/facebook/buck2
Rust support is also rather good ;) https://github.com/facebookincubator/reindeer But you need "fixups" to get many cargo packages to work with Buck 2: https://github.com/facebook/buck2/tree/main/shim/third-party...
One of the specific weaknesses I found with Bazel for embedded development was support for multiple toolchains was really not great. I'm sure it's improved, but I found the gn/ninja combination much better.
However, I'm procrastinating right now by reading HN instead of fixing our multi-compiler requirement toolchain setup for Bazel, and I am contemplating resigning from my job and IT altogether. I hate Bazel at this point.
If you find a good replacement for SW eng as a career, let me know.
Could you point us to some relevant discussions (GitHub, etc) so some of us can weigh in and possibly contribute to this pain point?
But basically: vendored (git submodule in this case) project that was workspaced, added as a dep inside a workspace... Blew up badly with general workspace complaints. And doesn't help that at work we have to accommodate working with older toolchain versions because we ship a hardware product with that requirement.
[1]: https://github.com/rust-lang/cargo/issues/7520
[2]: https://github.com/rust-lang/cargo/issues/7520#issuecomment-...
>is asked to clarify
>"I'm too busy!"
But sure, flame away, you're adding a lot to the conversation /s
Could you be clearer about this? What's wrong with cargo vendor?
(It's possible it's been fixed in recent Cargo versions, but that's not available to me.)
But in the end, cargo is a tool primarily designed for building crates that depend on crates in crates.io. Even workspaces themselves are a bit of a half-baked afterthought.
But cargo really should have started by looking at Maven for inspiration -- a) a moderated repository and b) Maven has much more expressive support for version restrictions etc.
Instead crates.io feels like it's going the way of the world npm.
Anyways, sorry if this sounds like "get off my lawn", I'm grouchy today.
└── num v0.4.1
├── num-bigint v0.4.4
│ ├── num-integer v0.1.46
│ │ └── num-traits v0.2.18
│ │ [build-dependencies]
│ │ └── autocfg v1.1.0
│ └── num-traits v0.2.18 (*)
│ [build-dependencies]
│ └── autocfg v1.1.0
├── num-complex v0.4.5
│ └── num-traits v0.2.18 (*)
├── num-integer v0.1.46 (*)
├── num-iter v0.1.44
│ ├── num-integer v0.1.46 (*)
│ └── num-traits v0.2.18 (*)
│ [build-dependencies]
│ └── autocfg v1.1.0
├── num-rational v0.4.1
│ ├── num-bigint v0.4.4 (*)
│ ├── num-integer v0.1.46 (*)
│ └── num-traits v0.2.18 (*)
│ [build-dependencies]
│ └── autocfg v1.1.0
└── num-traits v0.2.18 (*)That said, you’re right that “to keep binary size down” isn’t why the standard library is small. Heck, that you don’t compile from source means sometimes binaries are larger than they have to be, even with LTO! It is largely because anything that’s there has to be supported exactly as is forever, interface wise, and for many things, we did not have the confidence on what the right interface would be. Furthermore, it’s harder to maintain the standard library than it is a package out of tree for a variety of reasons.
~> rustup component list | rg src
rust-src (installed)
But this is so that that workflow works well, not because the compiler actually uses it during the build.> the compiler is smart enough to not be over-compiling unused symbols.
This is true even without the source, mind you, but the devil is just in the details. Stuff like the panic and/or formatting machinery is known to creep in even if you don't think you're using it, because some edge case might. This only truly matters for embedded and similar, but it's still a thing.
I think in Rust the choice is more one that originally prioritized viability of long-term maintenance of the project by keeping the volume and surface of the standard library small. For all more recent inclusion the inclusion criteria seem to be either "small ecosystem functionality that everybody is using and has proven to be valuable" (e.g. once_cell) or "what is close-to-optimal solution for critical parts of the ecosystem that we have to include to minimize ecosystem splintering" (e.g. the work around std::future). I wouldn't be surprised if there are actual written down criteria for std inclusion somewhere.
I’m not aware of the current state, but in the old days, it was roughly:
1. Is this useful in virtually every Rust program?
2. Is this something (data structures, mostly) that requires a lot of unsafe to implement?
3. Is this a vocabulary type?
Any of these is a slam dunk, anything else is more subjective.
That's too strict a criterion for a standard library!
You can tell that this isn’t literally true just by looking at the contents of the standard library.