Rust just imposes constraints on references using the type system, so the compiler knows to emit the free in the right spot. For regular application code there's no real benefit. It's not really a systems language because of all the hidden memory allocations and lack of a stable ABI. It's garbage for embedded because of all the gratuitous copies you have to make to appease the borrow checker. It blows up your memory budget.
Can't disagree, naive garbage collected code would outperform naive Rust code that allocates Strings and Vecs up the wazoo.
And performance isn't a real issue with simple CRUD/ORM-type services.
Wrapping business logic in newtypes and enforcing specific invariants for them and serde style "parse don't validate", can do wonders for business logic, but then again you can probably just use a garbage collected functional language with dependent typing and a bunch of other tricks in its hat, and get more mileage out of it, if that's the code you write.
> It's not really a systems language because of all the hidden memory allocations
C and C++(especially) can hide memory allocation just fine themselves.
Allocations are pretty explicit in Rust, but layers of libraries can hide those (if you don't use no_std and the like), just like in C or C++.
> lack of a stable ABI
there is the C ABI, a stable Rust ABI at any stage would just be an optimization killer and a massive PITA, ask C++. Stable ABI, international standard, and multiple implementations of the compiler, are not a requirement for a systems language.
For a language that relies on compile-time monomorphization, a stable ABI beyond what C already provides gives precious little. Swift ABI forgoes monomorphization, but it's not really a trade-off a systems language should embrace. The template header issue in C++, I won't even touch.
There are also crates in the ecosystem that can generate 2-way FFI glue code to provide a stable ABI for Rust based on extern "C".
> It's garbage for embedded because of all the gratuitous copies you have to make to appease the borrow checker. It blows up your memory budget.
We use embedded Rust on a memory constrained MCU and besides slightly increasing the maximum stack size (because less is allocated on the heap), memory budget is not an issue we have. And in LLVM 16, Rust's stack usage will decrease even more, due to recent optimizations.
You don't need to use "gratuitous copies", you can statically allocate just fine, and if your value is ephemeral and is read/mutated by multiple threads, heap allocating with reference counting is just fine.
(Not necessarily "better" overall, but a better choice for certain applications.)
Both are better than C but I’d expect a good programmer to be more productive in Rust and to avoid more logic errors (which may or may not be security issues), not to mention the many problems caused by widely used open-source libraries written during the bad old days which have extensive dependencies and often enable risky behaviors by default. Culture isn’t a core language feature but it matters.
A race condition might, but even then, I'm not sure how likely that is to result in a vulnerability in a garbage collected language.
Rust can have double-frees and some of the most serious memory safety errors that those cannot.
Basically, I’d say that at fond point this gets to be a lot more complicated because you’re talking about which sets of features & cultural practices make you more or less likely to make exploitable errors. Does using a GC buy you enough to balance out the complexity fetish common in the open source Java world? Which is safer probably depends on what type of projects you work on.
Isn’t the whole argument rust vs C++ that rust gives you memory safety even though it’s more complex?
Like if you can tolerate memory issues all of the sudden to avoid complexity then there was no argument against C++ in the first place.
That's also why I mentioned culture: for various reasons, a lot of Java programmers sought ways to make tons of behaviour configurable which also means that there's both plenty of scope for log4j-style mistakes and lots of optional behaviours which might be exploitable even if few people use them. Rust's culture tends not to encourage that kind of thing as much so even though certain types of errors are possible in any language they're not equally common.
This is not generally true of other languages, where it is allowed for the compiler to do whatever it wants in these cases, meaning that once you go down the happy path, nothing can be guaranteed. Also, safe rust preventing data races is again not that big of a thing because if you want to use the same memory region (not read-only) from multiple threads you will have to use unsafe either way.