Hello. :-) I started writing C++ around 1992, and was lead programmer on various C++ production systems from 2002 until 2011. I worked with valgrind, unit tests, and boost/std::tr1 C++, but not C++14 or 17.
For the past three years, I've been writing production systems in Rust. Here are some things I've observed:
- If your C++ code relies heavily on complicated webs of objects that all mutate each other (as found in many game and GUI designs), you'll have a bad time translating that code to Rust. If your C++ code tends to be more functional, idempotent, or transaction-based, and if it has clear ownership of objects, then translating to Rust will be much easier. For example, Rust seems to work better with ECS-based games and React-like GUIs.
- Rust is significantly weaker than C++ for designs which use integer template parameters (e.g., "vec<3>") or partial template specialization.
- Modern C++ allows you get ownership 99% correct, if you throw enough tools at it. Rust gets ownership 100% correct by default. If you're dealing with situations where that 1% matters (complex threading, or decoding hostile data), then it's a much bigger difference than you might think.
- Not counting Rust IDEs (which are only borderline OK by static language standards), Rust tooling is great. Dependency management is solid, linting is good, automatic formatting is good, etc.
- I've been working with nightly and experimental builds of Rust async code, and I think that Rust will soon give C++ a real run for its money in this space. Multithreaded async executors (where you mix "green" threads with OS threads) rely very heavily on precise tracking of who's allowed to mutate what, which Rust is excellent at.
> Finally, if a Rust veteran could let me know: how does Rust deal with general resource management in the presence of exceptions?
Rust does not have exceptions. Instead, it uses `Result<T,E>` and the `?` operator to propagate errors. Ownership and RAII are 100% idiomatic and fully checked by the same systems as memory management.
That's exactly the kind of c++ code you probably don't have to refactor/move to another language at all
Re: general resource management. It works exactly the same. Rust also does RAII. Rust also doesn't use exceptions for error handling.
One easy to state advantage of Rust over C++ is that it checks your threading design.
If you want to handle closing a socket, it’s as easy as implementing the Drop trait on your type. Without an unsafe block there’s no way your destructor won’t be called.
struct Socket(u16);
impl Drop for Socket {...}
To answer your question there’s no special casing around memory vs non-memory resources. Your socket is backed by a kernel ID which must exist in memory, of course, so by adding a Drop implementation you can then define the special treatment yourself. If threading considerations exist you can explore the Sync and Send marker traits too.
It’s largely a much simpler language than C++, you just have to learn how to write it. A lot of the friction/confusion IMO is that it looks so similar to languages you use at first glance you just start writing the code you used to in a Rust-y way, instead of Rust, then are frustrated as to why it won’t build.
In some ways it’d be clearer if it looked “foreign” like prolog — it’d be a lot less popular though haha.
Unfortunately, this is not correct. Resource "leaks", involving failure to drop a resource which is no longer in use, are possible in Rust, and the borrow-checker won't protect against them. They're most likely very rare in idiomatic Rust (the case I know about has to do with RC-cycles) but they're possible.
> forget is not marked as unsafe, because Rust's safety guarantees do not include a guarantee that destructors will always run. For example, a program can create a reference cycle using Rc, or call process::exit to exit without running destructors. Thus, allowing mem::forget from safe code does not fundamentally change Rust's safety guarantees. ... Because forgetting a value is allowed, any unsafe code you write must allow for this possibility. You cannot return a value and expect that the caller will necessarily run the value's destructor.