You'd be surprised to see, after deep inspection, how little of Rust you can remove while keeping its safety story the same (that is, memory safe without GC).
Traits? Nope. We need some way for code reuse. Classes cannot be made memory safe without extra cost (at least, I don't know how can they). And they are not less complex either. Templates like C++? More complex, and doesn't allow defining safety interfaces. No tool for code reuse? That will also severely limit the safety (imagine how safe Rust was if everyone would need to roll their `Vec`).
The borrow checker of course cannot be omitted. ADTs are really required for almost anything Rust does (and also, fantastic on their own). Destructors? Required to prevent use after free.
Async can be removed (and in fact, wasn't there in the beginning) which is a large surface area, but even today it can mostly be avoided if you're not working in some areas.
I don't think anybody can deny Rust is complex, but most often it's inherent complexity (what you call "sophistication") given the constraints Rust operates in, not accidental complexity.
Absolutely this. Folks are used to an awful lot of the complexity being hidden from them through avoidance of threading, runtimes, garbage checkers, standard libraries, and so on. For a language which exposes all of the complexity, Rust feels minimalist. C++ is one of a small number of other languages which also expose all the complexity, and it feels gargantuan and like poorly-thought out additions after additions by comparison. I don't mean to disparage the C++ devs at all, C++ has managed to be useful for ~40 years, and it's still capable of incredible things. Just that we've learned a lot over those 40 years, and computational capacity has grown significantly, and Rust has had the opportunity and architecture to integrate some of that learning more fundamentally.
Somehow most of the libraries in the Rust ecosystem seem to interoperate with each other seamlessly, and use the same build system, which I didn't have to learn another unrelated language to use! Astounding!
Says who? You can totally do code reuse using manually-written dynamic dispatch in "rust without traits". That's how C does it, and it works just fine (in fact, it's often faster than Rust's monomorphic approach that results in a huge amount of code bloat that is often very unfriendly to the icache).
Granted, a lot of safety features depend on traits today (send/sync for instance) but traits is a much more powerful and complex feature than you need for all of this. It seems to me like it's absolutely possible to create a simpler language than Rust that retains its borrow checker and thread safety capabilities.
Now whether that'd be a better language is up to individual taste. I personally much prefer Rust's expressiveness. But not all of it is necessary if your goal is only "get the same memory and thread safety guarantees".
> Says who? You can totally do code reuse using manually-written dynamic dispatch in "rust without traits". That's how C does it, and it works just fine.
Rust can monomorphize functions when you pass in types that adhere to specific traits. This is super-handy, because it avoids a bounce through a pointer.
The C++ equivalent would be a templated function call with concept-enforced constraints, which was only well-supported as of C++20 (!!!) and requires you to move your code into a header or module.
Zig can monomorphize with comptime, but the lack of trait-based constraint mechanism means you either write your own constraints by hand with reflection or rely on duck typing.
C doesn't monomorphize at all, unless you count preprocessor hacks.
The only things in Rust that are real statements are `let` statements, and item statements (e.g. declaring an `fn` inside a function). All other statements are in fact expressions, although some always return `()` so they're not really useful as such.
Rust is susceptible to segfaults when overflowing the stack. Is Rust not memory safe then?
Of course, Go allows more than that, with data races it's possible to reach use after free or other kinds of memory unsafety, but just segfaults don't mark a language memory unsafe.
Go is most emphatically NOT memory-safe. It's trivially easy to corrupt memory in Go when using gorotuines. You don't even have to try hard.
This stems from the fact that Go uses fat pointers for interfaces, so they can't be atomically assigned. Built-in maps and slices are also not corruption-safe.
In contrast, Java does provide this guarantee. You can mutate structures across threads, and you will NOT get data corruption. It can result in null pointer exceptions, infinite loops, but not in corruption.
This is just wrong. Not that you can't blow up from a data race; you certainly can. Simply that any of these properties admit to exploitable vulnerabilities, which is the point of the term as it is used today. When you expand the definition the way you are here, you impair the utility of the term.
Serious systems built in memory-unsafe languages yield continual streams of exploitable vulnerabilities; that remains true even when those systems are maintained by the best-resourced security teams in the world. Functionally no Go projects have this property. The empirics are hard to get around.
There were CVEs caused by concurrent map access. Definitely denials of service, and I'm pretty sure it can be used for exploitation.
> Serious systems built in memory-unsafe languages yield continual streams of exploitable vulnerabilities
I'm not saying that Go is as unsafe as C. But it definitely is NOT completely safe. I've seen memory corruptions from improper data sync in my own code.
Go ahead and demonstrate it. Obviously, I'm saying this because nobody has managed to do this in a real Go program. You can contrive vulnerabilities in any language.
It's not like this is a small track record. There is a lot of Go code, a fair bit of it important, and memory corruption exploits in non-FFI Go code is... not a thing. Like at all.
Go is rarely used in contexts where an attacker can groom the heap before doing the attack. The closest one is probably a breakout from an exposed container on a host with a Docker runtime.
If you're certain, demonstrate it. It'll be the first time it's been demonstrated. Message board arguments like this are literally the only place this claim is taken seriously.
That's not an apple-to-apple comparison, since Rust is a low-level language, and also because `reqwest` builds on top of `tokio`, an async runtime, and `hyper`, which is also a HTTP server, not just a HTTP client. If you check `ureq`, a synchronous HTTP client, it only adds 43 packages. Still more, but much less.
And in Go I can build a production-ready HTTPS (not just HTTP) server with just the standard library and a few lines of code. (0 packages).
That Rust does not have standard implementations of commonly-used features (such as an async runtime) is problematic for supply chain security, since then everyone is pulling in dozens (or hundreds) of fragmented 3rd-party packages instead of working with a bulletproof standard library.
And this is exactly why Go is winning: because it's actually rather easy to write "pure Go" utilities (no dependencies outside the standard library), which statically compile to boot (avoiding shared libraries).
FWIW, there is an accepted proposal (https://github.com/rust-lang/libs-team/issues/394) to add random number generation to std, and adding traits like in `num-traits` is wanted, but blocked on inherent traits.
Traits? Nope. We need some way for code reuse. Classes cannot be made memory safe without extra cost (at least, I don't know how can they). And they are not less complex either. Templates like C++? More complex, and doesn't allow defining safety interfaces. No tool for code reuse? That will also severely limit the safety (imagine how safe Rust was if everyone would need to roll their `Vec`).
The borrow checker of course cannot be omitted. ADTs are really required for almost anything Rust does (and also, fantastic on their own). Destructors? Required to prevent use after free.
Async can be removed (and in fact, wasn't there in the beginning) which is a large surface area, but even today it can mostly be avoided if you're not working in some areas.
I don't think anybody can deny Rust is complex, but most often it's inherent complexity (what you call "sophistication") given the constraints Rust operates in, not accidental complexity.
reply