The Case for Rust in the base system
mail-archive.freebsd.org
mail-archive.freebsd.org
That said, I'm a big Rust believer and think it's the right tool for the job in a lot of situations where the historical answer was C or C++. Maybe it's the right tool for FreeBSD, too.
oh, my sweet summer child...
That no to say that Rust shouldn't be considered, but maybe initially for new development or selective rewrites. Some of the mentioned, like nscd or devd in particular, shouldn't be that high on the list. The ZFSd should be fairly safe, as that's mainly needed on the larger platforms.
This is also why we LTS versions.
Sure, it's nice to be able to tinker and hack to see if you can upgrade them yourselves, but is it worth maintaining architecture support for that kind of use case? Not asserting that is definitely not, but I think it's worth considering.
So the main set of platforms you're looking at are ones that are functional enough to have modern, stable C compilers, aren't too quirky that reasonably portable C code would "just" work, and yet ones that you don't already have support for in Rust. Outside of the embedded space, there's like... fewer than 5 architectures left, the most notable of which probably being Alpha.
Of course, this only means we could, it doesn't necessarily mean we should.
E.G. Curiosity rover is doing just fine running on millions of lines of C.
https://vdocuments.mx/monitoring-the-execution-of-space-craf...
If it's going to impact OS stability and decrease performance and portability of the humble, dependable, simple C, it doesn't belong in the core. C is better than Rust for OS development.
E.g. at one end of the spectrum you have Python where you have to write explicit tests for typos and type errors.
At the other end of the spectrum you have formal verification languages like Dafny that may not require any tests to be written.
Rust is somewhere in-between. It has a very strong type system and lots of features that make memory errors and business errors less likely than in most other languages. You still need some tests, but not as many as with C/C++, Python, JavaScript, etc.
It's much more feasibly to be secure by construction.
> According to research, up to 850 billion lines of COBOL code are currently running in nearly 30,000 organizations, typically in critical production environments. 90 percent of Fortune 500 companies rely on it. Never has there been this much COBOL in circulation and the volume is only likely to increase for the foreseeable future.
https://www.chrly.pt/en/2023/06/14/cobol-the-immortal-langua...
Rust is not immune to security vulnerabilities. And at the end of the day, social engineering will steal more data than "hacking the mainframe". Why break in when you can just ask to be let in?
OpenBSD has a great security track record because they resist excessive change and prefer simplicity. For those who want to add Rust to the core of FreeBSD my primary question: is it really necessary? Or is it just because a bunch of Rustaceans want to?
I want to point out one more thing: Rust is not a simple language by any stretch. It's equal to in complexity to C++ (yet without decades of established "good practices"). It is much preferable to have an easier to understand core and move the complexity outward—for improved stability and robustness. The core OS by nature of what it does needs to access raw resources in an "unsafe" manner. Rust kernel code will be littered with unsafe blocks and unnecessary complexity.
Sure.
But running on OpenBSD doesn't solve application level vulnerabilities. And sure - OpenBSD may help limit the ability of an attacker to leverage one vuln into compromising the entire system. But if the original attack was important enough, that's cold comfort.
Bfha... the link is a thread by the FreeBSD devs. Stop it with this language evangelist strawman.
I don’t think it was your intention, but what you just said makes me want to applaud more heavy-handed efforts to make the switch away from C/C++.
So COBOL everywhere it is. Let it be written, let it be done.
There is 32-bit x86 tier 1 targets like i686-unknown-linux-gnu.
> The only Tier 1 architectures are [...] only the absolute mainstream. Not exactly a bastion of architecture support.
Tier 1 means that the target has the full testsuite run against it for each single pull request. Running the CI is computationally extremely complex, and it is hard to provide a reliable and fast CI service for most architectures.
This seems very much within the realm of the FreeBSD’s capabilities.
Most new platforms port to Linux, commit drivers to the Linux kernel, and ignore the other platforms. The other platforms don't really seem to bother anymore, unfortunately. Things have bit-rotted to such an extent that FS will destroy large amounts of data and.. nobody seems to notice. Kernel panic reports are ignored. Debuggers fail to set functional breakpoints on native hardware. CPU quirks that need workaround to prevent panics are never committed.
Meanwhile, developing this kind of expertise is about as far from the minds of younger experimenters and hackers now as baking cakes and permaculture gardening were from the 00s hardware hackers decades ago. Not only is it far from the minds of younger experimenters, but even attempting to develop it hits a hard wall of hardcore, draconian behavioural purists, secops fashionistas who deride essentially any burgeoning attempts at writing drivers, and language revolutionaries who rage extensively at people for writing any new code in any language but their own favourite.
The situation is pretty harsh and it's likely not going to get any better anytime soon.
For everything above the line, Swift is definitely viable today. Expect shiny new stuff from Apple to be Swift first, or even sometimes Swift only. If you're an Apple First developer, you should learn Swift, or you are going to be relegated to maintaining legacy software in the next maybe 3-5 years.
Microsoft is already solidly on the Rust bandwagon. So yes, Rust for NT is very possible. Unlike Apple though the Microsoft corporation has always been able to do a bunch of things at once, even if it doesn't always do them all well -- it doesn't need a singular focus to achieve success. So don't expect Microsoft to magically pull effort off C++ and .NET to do more Rust.
Apple has been using Rust for years, and I'm not aware of any decisions to move away from that, though given how secretive they are, I guess that could be a possibility.
> So yes, Rust for NT is very possible.
Rust is already being shipped (in preview) in Windows' kernel.
"Introducing a Memory-Safe Successor Language in Large C++ Code Bases"
This is an hour long talk. I don't see anything in the slides that indicates that Apple management has decreed that Swift is the sole successor language to C++, forsaking all others, and winding down their Rust codebases. Could you help me find out where that is in this talk?
It is all about C, C++, Objective-C and how Apple is replacing them with Swift.
Rust isn't even shown on languages in use at Apple, https://youtu.be/lgivCGdmFrw?t=284
As for tracking down the exact minutes of each Swift reference, maybe I will try to find some time on my side to prove me right on the Internet.
- Overview of C, C++, Objective-C and Objective-C++ history on Apple platforms
From https://youtu.be/lgivCGdmFrw?t=169 to https://youtu.be/lgivCGdmFrw?t=306
- How Swift fits into the picture
https://youtu.be/lgivCGdmFrw?t=317
Key statements.
"Apple is very serious making Swift viable anywhere on this chart..."
Meaning a chart showing "UI Apps, Portable utililities, Platform libraries, Portable libraries, Kernel and below."
"...that is because we feel we need a successor language anywhere on this chart, so we are putting Swift at the kernel and below, as well as, platform libraries, and not just UI application, and we think it is a very important future for the language, we are very commited to it"
Up to https://youtu.be/lgivCGdmFrw?t=352
- Basic Expectations
"We need to focus on adding one sucessor language"
"Every language we add comes with extra complexity, in terms of tooling, XCode support, build system support and so on, as well as, language problems making them interoperate"
"We have four precessor languages, we don't want four sucessor languages"
"Should be accessible as first language, for people coming into our platform"
From https://youtu.be/lgivCGdmFrw?t=360 to https://youtu.be/lgivCGdmFrw?t=411
"At the same time, if we want to make it usable in kernel, we need to provide enough control to make it usable in such environments"
"In particular that means being able to scale down to a minimal runtime"
From https://youtu.be/lgivCGdmFrw?t=420 to https://youtu.be/lgivCGdmFrw?t=493
- Problems with C
https://youtu.be/lgivCGdmFrw?t=493
- Overview of Swift
"So we feel pretty strongly, obviously at Apple our sucessor language is Swift and I am here to talk about features of Swift, both to try to sell you on to it, but also to talk about the things we think are pretty necessary and the ways in which a programming language can support, you know, clear code, safer and more correct code."
"Like I said before, Apple has always intended for Swift to be a sucessor language for all of our predecessors, from the top to the bottom of our stack, accessible to novices, powerful enough for experts, it is real a tall order.
From https://youtu.be/lgivCGdmFrw?t=1996 to https://youtu.be/lgivCGdmFrw?t=2042
Quite clear that Swift is the future and zero mentions of Rust.
"Introducing a Memory-Safe Successor Language in Large C++ Code Bases"
It remains to be seen if Rust/WinRT will ever take off, given the mess that the same team has done with C++/WinRT, now in maintenance.
.NET based GUI frameworks seem to have won to that front, no wonder given how bad the C++/WinRT experience end up being, and Rust/WinRT is years away to achieve parity with C++/WinRT, let alone what .NET GUI frameworks can offer today in ecosystem, and GUI components.
Where Microsoft is going big on Rust, is Azure, where Rust is now the official systems language for new development on use cases where managed compiled languages aren't a fit.
This was announced at Prossimo's conference in memory safety systems, by David Weston, the vice president of OS security at Microsoft.
https://twitter.com/dwizzzleMSFT/status/1720134540822520268?...
Can anyone comment on why this is impossible in C?
> I don't think anybody has claimed this yet. But I _have_ made a similar claim, that some things can't be written in C. I'll elaborate on the project that started this thread: the fusefs test suite. When I designed the fusefs test suite, I based it around the priniciple of Mocking. Each test case has a client thread that does normal file system access, and a server thread that implements a fuse file server. But it isn't a full implementation of a fuse file server; it's just a shim around a mock object. For each test case, the file server's behavior is programmed with expectations. The mocking technique is so limited and awkward to use in C that almost nobody bothers. Just compare the documentation for CMockery (C) with Mockall (Rust). C++'s Googlemock is somewhere in between.
https://lists.freebsd.org/archives/freebsd-hackers/2024-Janu...
Edit: after some googling I found https://lists.freebsd.org/archives/freebsd-hackers/2024-Janu... but I couldn't read the whole thread at once
That the very first thing to do is have to fix the compiler and build environment, and only then start to actually use it, is an additional complication.
https://doc.rust-lang.org/nightly/rustc/platform-support.htm...
Just in case anybody else can not see the post. I have no idea if the post is hidden, deleted, or if the server is overloaded.
- The Case for Rust in the FreeBSD Base System
Most Linux folks are not aware of what 'base system' means - and that its related to FreeBSD.
About the Rust inclusion - I would include it - and would add these targets:
# make buildworld (without Rust)
# make rust (like Rush without 'buildworld')
# make buildworldrust ('buildworld' + Rust)
Regards.I run FreeBSD on my servers and use scripts to manage jails and send and receive ZFS snapshots between hosts.
I've been wanting to write some more robust tools of my own for those things, than the scripts I have now, and my favourite programming language is Rust!
- A stable target, and
- Lots of FreeBSD specific Rust code to read from expert FreeBSD programmers, as guidance
- Probably better ability to build ISOs of my own that include the base system and my jail and ZFS management Rust programs, so that I can install servers from that ready-made ISO and have less post-install work to do
The FreeBSD project tends to like to slim the base system down. E.g. some years ago they removed Perl from the base system. So out-of-the-box the only scripting language that’s available is shell. In contrast, OpenBSD has more stuff in its base system and on the subject of Perl they doubled down on it (their package manager e.g. is written in Perl). It’s also visible, when you download the base system repos for each of them and run tokei to find out the numbers of LOC. (I don’t remember the numbers off the top of my head.) That said, FreeBSD still includes a full LLVM toolchain due to the rule “base builds base”, and LLVM is... a lot. It’s the biggest source of bloat in my jails to be honest. I keep meaning to find out if I can remove it from them.
Advanced typing
Build tools
How does that differ from RAII?
I think i misunderstand you or lack knowledge, because this sounds exactly like RAII.
I know that rust has major compile time checks, but saying that the difference is that it reason about life time as difference to c++ is misleading. I think the major point of c++ compared to c is that c++ "reason about object lifetime statically" with deconstructors and RAII. And saying that rust do this and implying c++ don't is misleading.
One thing C++ programmers might be interested to learn, is that this doesn't only apply to simple variables and references; it also applies to library data structures and their methods. The Rust compiler doesn't really "know" what the .clear() method on a Vec does, but it knows enough to prevent you from calling that method while you're holding references to the Vec's elements.
It’s also a big part of C and C++’s learning curves, it’s just that the compiler doesn’t tell you about it; you’re being taught by segfaults and silent memory corruption instead.
There is one major difference. In Rust you can memcpy objects you own to a different base address and they will still work, unless they're pointed at by a Pin<> type (as such, code that requires objects to stay put in memory must take a Pin<> reference. This arrangement may potentially be replaced by a special "?Move" trait in future editions of Rust). C++ pins all objects by default and allows objects to have custom "move" constructors, to sort of enable them to be moved elsewhere.
In C++, objects that get "moved" must leave behind something that can be destructed cleanly; Rust has no such limitation, at least wrt. ordinary moves. Arguably the closest thing it has to a C++ move is the .take() pattern, which leaves a default-constructed object behind. But this is rarely used.
The general tradeoff is that the Rust pattern memcpy's compound objects more often, but has less gratuitous use of the heap and less pointer chasing compared to idiomatic C/C++. This is a valid choice once one has the compiler support that Rust provides.
An other major safety difference is that Rust uses destructive moves, once a binding is moved-from it becomes invalid / inaccessible (and it won't be destroyed).
In C++, a moved-from object is in a "valid but unspecified state", so it must be destructible (because destructors always run) but any interaction with the object other than destruction may be UB, and the compiler won't tell you (in the same way it won't tell you about borrowing issues whether it's UAF, invalidation, ...).
It seems that some Rust folks are still on Mozilla payroll.
Mozilla is still a member of the foundation, so they do support it in that sense, but they are one of many companies that do so.
1) Memory safety. If you live in the world of kernels and embedded code, your options are mostly C, C++, and (as of just the last few years) Rust. Of those, only Rust reliably prevents memory corruption mistakes like use-after-free.
2) Being a 21st century language. Rust has a lot of features that any new language would be expected to have these days: a unified build system, a library ecosystem that the build sytem knows about, slices over arrays and vectors, UTF-8 strings, convenient ways to de/serialize JSON, something like async/await, etc. This makes it competitive with languages like Java and Python for doing "everyday stuff", where realistically ~no one would use C. The main downside of Rust in a Java/Python sort of context is that the learning curve is a lot steeper.
A couple other notes:
- Some of nice 21st century features (like JSON) go away when you're writing embedded/kernel code, but others still work. Embedded ("no_std") Rust still has enums, error handling, slices, iterators, and sometimes even async/await. Those quality-of-life features are hard to replicate in C, even if you believe your code is 100% bug free :)
- If you know you're going to be writing multithreaded code, Rust's thread safety features are really unmatched. A lot of the same machinery that gets used for memory safety also turns out to be useful for thread safety. Libraries like Rayon make it surprisingly convenient to write multithreaded code, while also catching a huge percentage of mistakes at compile time. (Deadlocks are still possible though.)
Another very naive question. Are memory safety concerns from a cybersecurity standpoint or from less-dev error standpoint?
The dev-error is broader language guarantees, which dovetail into memory safety but are useful on their own e.g. data race safety, rich type system, ...
Cybersecurity issues make Rust a good choice for writing parsers for example. File format parsers handle scary data from the internet (JPEG, X.509, etc), and those often contain addresses and offsets that turn into raw pointers in the implementation, so they're a common source of security bugs. At the same time, they're usually isolated behind a reasonably tight API, so it can be practical to rewrite them in Rust even when all the calling code is still C or C++ or even Python.
On the other hand, fewer-mistakes-sucking-up-dev-time is an especially big deal in multithreaded code. There was a story from Mozilla about how they'd tried to add threading to part of Firefox (I think it might've been the CSS engine?) and failed multiple times, but then they succeeded in Rust. Threading bugs tend to be painful to reproduce, and Rust's compile-time checks are extremely valuable there.
These memory-related exploits disappear with Rust aside from in "unsafe" blocks (possibly the worst named keyword in any language... it should have been called "trusted"), and that means you have a smaller and more easily auditable attack surface for these types of memory-related exploits. Some code (e.g., FFI) can't be verified as memory-safe by the Rust compiler at compilation time so "unsafe" is there as an escape hatch. I've written a bunch of Rust since 2014 building things like webservers, realtime futures processing algorithms, MEV bots, etc., and I've only had to use "unsafe" a few times.
I've also worked in security on products at Fortune 100 companies, and C is a constant nightmare for CVEs. I think I have PTSD from having to update libcurl. The more software we have written in memory safe languages, the better off we all are.
It’s probably worth nothing that that vast majority of, and of large scale attacks, is basic shit that has nothing to do with memory safety.
There’s benefit to memory safety, but just cause you’re wearing a helmet doesn’t mean you’re completely safe.
Additionally, it’s not Google overall, but chromium project that find this, as well as not Microsoft overall, but specifically Windows. These are two projects that will naturally see a much higher rate of memory safety issues.
This isn't the case: https://msrc.microsoft.com/blog/2019/07/a-proactive-approach...
> Since 2004, the Microsoft Security Response Centre (MSRC) has triaged every reported Microsoft security vulnerability.
They've never qualified it as Windows only, to my knowledge.
1. Rust has a more modern sense of computer primitives than C does. For example, C has the char-short-int-long-long long set of types which correspond to 8, 16, 32, and 64 bit integers in practice (and yes, 5 types for 4 sizes), whereas Rust has u8/u16/u32/u64. Rust also has a builtin slice type, which is a desperately needed notion of pointer + size of array, and C's lack of such type has proven to be the source of much malware. The standard library is also somewhat richer too, you have better support for things like threads or networking than C/C++ does. But note that Rust is still like C in that it has a very thin runtime layer, and can also go a step further and have "no" standard library at all, which is necessary in several contexts.
2. The borrow checker. Effectively, use-after-free (or any other lifetime issue) vulnerabilities become compiler errors, and even many kinds of data races are also turned into compiler errors. Also it can do things like make iterator invalidation issues a compiler error as well.
1. A type system that's deeply inspired by OCaml, a language that's designed and used by a lot of programming language research people and is widely praised (just look at how every other language — C#, Java, Kotlin — is suddenly trying to pile half baked imitations of ML features like ADTs and deep pattern matching onto their existing OOP type systems), except they've both imemented the type system correctly and fully, and also filed off all the warts and awkward features, and replaced OCaml's verbose and underpowered first class module system with Haskell style type classes
2. got amazing, highly flexible and generalized iterator and monad support (you can use iterator methods on monads), basically second only to Haskell itself, that compiles down to the equivalent of hand written assembly code loops
3. an error handling system using monads that neatly sidesteps the issues with both traditional C style error handling and exceptions that combines with point two in an exponential curve of awesomeness for error handling
4. hygienic (from Racket) and procedural (eg Common Lisp) macros
5. And finally ofc their statuc memory safety and race condition safety, which is based on linear types ala Idris.
But most importantly, Rust's designers seem to IMO have carefully picked only the advanced programming language features that tend to make your code clearer as well as more correct, and help you model problems at the right level of abstraction to be intuitive, and also only features that can be implemented in a way that is as performant as the equivalent C++ code, roughly speaking at least. They also seem to have been very careful with how they integrate and implement everything in order to uphold those goals.
So you get to have 90% of the power of a language like Haskell, in a language that performs like, and has the low level capabilities of, C++, with the extra benefit of — for instance — less dogmatic puritanism and more deterministic and comprehensible performance and execution properties then Haskell, and less Lovecraftian horror full of surprise gotchas than C++.
It's kind of like the ultimate pragmatic language for someone like me who has gone deep into pure functional languages and other esoteric languages and wants to have as much as of that goodness as is practical.
A data race goes like this: At least two simultaneous execution contexts (maybe threads for example) are looking at the same object X and at least one of them changes it, but there is no particular order of these events agreed between these contexts.
In your head, even with parallel computing everything seems to happen in some sort of global order. A happens before B, or B happens before A. This is called Sequential Consistency, and it's a lie, the machine doesn't actually work like that, but humans can't really understand non-trivial software without this lie, so, all our high level software (and when I say "High level" I mean like the C Programming Language) pretends sequential consistency is always preserved and goes to some lengths to achieve that.
In Rust you can go about your business. (Safe) Rust promises this is true and it'll make damn sure. But in many languages like C or C++, actually you can very easily inadvertently construct a data race, revealing that it was a lie and if you do so all bets are off. Since you probably can't reason about your program's behaviour anyway, they figure "fuck it" and that's Undefined Behaviour.
Bonus round for languages which do better than most: Go says if you race a trivial object like an integer, you lose Sequential Consistency but this isn't automatically UB. Complex races are UB.
Java says races are never UB, but they do lose Sequential Consistency. Your Java Program is now very, very difficult to understand, but it's not nonsense.
OCaml goes furthest, it says your race isn't UB and it offers very tight constraints on what's wrong. OCaml's work on this is relatively new, so it may be a while before we're confident whether this is a manageable situation normal humans (well OCaml programmers) can handle.
Yes, thank you for pointing that out, ive had a bad headache for most of the day and knew I was saying the wrong term when I wrote it, but couldn't for the life of me remember what the right one was! And yeah I'm pretty familiar with this, although I wasn't aware of OCaml's strides
As I understand it, Rust lacks do-notation and higher-kinded types, making it hard to have true monads in Rust.
Scala would be the language with monad support comparable to Haskell.
Yes, it is very much true that rust cannot yet represent monads as type classes directly, since it does not have any form of higher kinded types yet — which is the reason why I said second only to Haskell. If it could represent monads directly, than I would have placed it equal to Haskell — as I would have placed scala!
But I do think it is important to highlight that it does have pervasive useful monad use in the language, and an interface that allows you to use a very large common set of powerful transformations on them via the iterator trait, and it uses the monads that exist in the language and the ability to use that trait on any monad a lot to increase the power of the language in a way no other language besides ones with superior monad support does that I know of. So I would argue that while it is still second to Haskell, it is still far above almost any other language not equal to Haskell in this respect. Which is what I meant by saying second only to haskell!
Also, HKTs are something that the rust team seems to be very actively looking to rectify with an in progress implementation currently available in nightly, so that won't be true for very long.
I believe that a monad is basically any container that has both a constructor and a flat_map function (and there may be one other requirement that I've forgotten). The special ability that Haskell has is the ability to write code that is generic over any such container.
`map: forall a b . (a -> b) => (m a -> m b)` and `pure: forall a . a -> m a`, plus some (mostly very natural) compatibility conditions between the three: `flatMap(pure)` does nothing, `map` takes the identity to the identity, etc.
It's definitely verbose, but underpowered? Modules are just as powerful as Haskell's typeclasses (and therefore more powerful than Rust's).
In my opinion they are, because any group of types and functions that implements a module signature must be wrapped up into a module that satisfies that signature at the point of use of any value that needs to satisfy that module signature, instead of a type just satisfying the given signature if the correct functions exist to manipulate it, which means in my opinion there's a lot less flexibility and it works more like constructing a new value of a certain type then it does having constrained parametric polymorphism in the way Haskell does. I think this is why although they technically can be used to imitate rust traits or Haskell type classes and allow a function to polymorphically take any type of that satisfies a certain interface, it isn't in practice used like that almost at all, whereas it absolutely is in Rust and Haskell. Witness for instance how both rust and Haskell have a generic type class for iteration that allows you to take anything that is iterable and use the whole module of functions available for iterables on it, so for instance in Rust you can use many of the same methods on a vector and an array and error checking monads, whereas in ocaml, even though something similar is in principle possible, it's almost never done that I've seen, because you have to essentially declare that I type implements a signature and how it does on every point of use where you want to pass a value of that type into a function that requires something with a certain signature. Instead functions are duplicated between modules for different types, like lists and arrays. Maybe that isn't underpowered per se, but it's certainly a practical consideration in the use of first class modules that mitigates a lot of what they are theoretically capable of that seems in my opinion to fall directly out of the conceptual model of using first class modules to do constrained parametric polymorphism, since values then must become modules.
As for the ability to represent the signature of monads — yes both ocaml and Haskell can currently do that while rust cannot, but there is actually currently active work on an RFC that would rectify that. So I suppose overall you might say that rust's type system is currently less powerful then the other two, but I really don't think that will be true over the long run, and it certainly isn't true of necessity, and rust has a type system with a great many benefits in practical power over OCaml's otherwise, in ways I feel impact day to day use more.
Second only in monad support to Haskell and equivalently powerful languages e.g. OCaml and Scala, but much better than mainstream languages, I should've said.
Also, I should have said that Rust helps with data races not races in general.
Also, I should've said Rust's trait system was more practically powerful / usable that OCaml's first class modules and potentially more powerful overall but not quite there yet.
This is what comes with writing enthusiastically, while suffering from a headache and brain fog. I swear I'm not an idiot and know my pure functional languages!
FreeBSD : Go :: Linux : Rust
Rust and Linux are changing rapidly and aren’t as worried about completely uprooting entire systems. See for example sys v init vs systemd.
Go didn’t get generics until 1.18 and promises backwards compatibility.
In Rust, the popular command line parser Clap completely changed its API between 3 and 4 such that you’ll need to update your code.
As much as I like predictably and simplicity, I think Rust will largely be more successful similar to Linux winning over FreeBSD.
I will admit that because the library ecosystem in Rust is younger, you do see things like Clap's breaking changes more often than in other languages.
In contrast, Linux and Rust are both smaller core systems with more exposed complexity, but also require a large user space to make the core useful. Linux is just a kernel so you gotta bring everything else in the OS. Rust you need libraries for things most other languages (including Go) include, like regex, async, etc. However both Linux and Rust have a culture of adapting the system to work exactly how you like, in Linux again you bring everything you want - want systemd? Cool. Don’t want systemd, that’s cool too. In rust, people go absolutely crazy with macros and abstractions and likely it will still be plenty fast at runtime.
It's much harder to manage a large codebase/application with Python, but it excels at data science and research programming, which Go and Java do not.
(Obviously, there is overlap between all, and you could do anything in any language.)
Rust’s close alternative is C++. Maybe also C? I don’t know if Rust can fill its niche of ultra-portable libraries that are relatively easy to import and use in tons and tons of other languages & systems.
Sure, there will be a small C ABI shim but the developer experience writing in Rust I think is worth it.
Also, nowadays whenever I want to write a fast Python extension, I don't think twice before using pyo3, super simple to write and build.