I worked on Google Earth for a few years, and the core was in C++. With ASan and modern C++, memory safety bugs rarely slipped into production... perhaps once every 3-4 months, and this was a million line codebase with 20 engineers working on it.
When you consider the cost of a memory safety bug (some users get odd behavior), how rare they happen (because of our good test coverage), and that everything's sandboxed in WASM anyway, there's just not much cost to this kind of memory unsafety bug... and certainly not as much cost as a full rewrite.
RIIR makes sense for projects whose memory unsafety causes catastrophic failure. And even then, in some of those cases it makes more sense to use Java or Javascript, which don't expose unsafe operations.
RIIR makes sense if someone's doing something with extreme performance requirements, and they're okay with a tiny risk of memory unsafety (from unsafe code).
All just my opinion though, I'm sure there are many other great opinions out there.
Presumably, without the potential for memory-safety bugs, there'd be no need for anything like a WASM sandbox, no?
If so, then can't the costs of the WASM sandbox (overhead, potential exploits in the JS/WASM runtime, etc.) be tallied under the column of "costs of memory-safety bugs"?
In about the same sense that e.g. the costs associated with running a Web Application Firewall in front of a Wordpress instance, can be blamed on how vulnerable/exploitable Wordpress+PHP are as a stack.
If you avoid introducing an attack-surface in the first place, then you don't need to spend effort on guarding it.
In the context of a web browser, the browser JS execution-context is itself already a sandbox, so running WASM there isn't necessarily doing any sandboxing per se (although it can); it's more just serving as the current state-of-the-art way to host arbitrary native code in the browser. (Too bad about PPAPI.) You'd still be using WASM here whether there was any sandboxing benefit or not.
I was instead more thinking about the Google Earth "native" desktop + mobile apps. Those are the environments I presumed you meant when you said that Google Earth is "sandboxed by WASM." Those deploy environments are where adding an intentional sandbox layer would get you some additional safety, over just having the user run a "raw" native C++ executable on their device.
We didn't rewrite iOS/Android either, any memory unsafety bugs just cause odd behavior on that user's device, and wouldn't e.g. give an attacker root access to our servers. The costs of the occasional memory unsafety bug didn't seem to approach the cost of a rewrite.
I should say, rewriting servers (especially public-facing ones) in a memory-safe language like Rust or Java is a much more reasonable proposition, but for client code, it's a slightly different story.
In an actual business, one has to justify a project's existence, by showing growth, new features, new users. Imagine stopping that while we rewrite a million line codebase. Now imagine a project which doesn't bring in revenue, and just delivers social good, while having out-of-touch executives looking at our resources and headcount like hungry vultures.
Rewriting in Rust would have cost us years, and would have killed Google Earth.
It's not something that has to be done wholesale, obviously. Language interop between Rust and C++ is constantly improving, with one goal being to make rewrites happen gradually while keeping the same interface wrt. outside C++ components.
The boundary between C++ and Rust code is often a lot trickier than one might think, because one has to reconcile conflicts between C++ paradigms and Rust paradigms... when one considers all of the temporary code that reconciles those conflicts, we see that the cost just went up.
And then moving that boundary is tricky to do in a healthy way, especially when using proper A/B testing practices which require us to have both the old and the new code alive in the same binary.
Rewriting often sounds like the solution to things, but in practice it's a very risky and costly thing to do. See also: https://www.joelonsoftware.com/2000/04/06/things-you-should-...
What am I missing?
That's across Java, Go, C#, Python. Maybe there's a GC language I should be using where all this feels easier, but if so I don't know what it is.
Having to create borrow checker friendly data structures doesn't bring anything to the table when writing GUI applications (including design tooling), distributed applications (Akka, Orleans, Erlang style, RDMS SP), CLI tooling, compiler tooling....
In what concerns languages with automatic memory management and support for value types, my list is somehow different, D, Nim, C#, F#, Swift, Go, Linear Haskell, Ocaml with Effects, Eiffel, Common Lisp.
And I leave Herb's talk regarding those issues with RC types,
"CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”
Even the first chunk of Herb's talk, before you get to reference counting - illustrates why you might feel more comfortable in another language compared to C++, as once again C++ has the wrong defaults. Rust's defaults are better.
Nullable shouldn't be the default. It's really hard to undo this bad decision, it's not one of the easy ones where I think C++ realistically could (but still won't) just fix it, but it's still a mistake. By getting this wrong C++ adds an unnecessary burden.
Since Rust doesn't get this wrong that burden isn't present. I am never worrying that the Box<Thing> in a Rust structure might not be there, by definition it's a Thing in a Box, if it might not be there that would be written Option<Box<Thing>> and the compiler would let me know if I ever forgot to account for the None case.
And it keeps happening. Later Herb wants an array of known-at-runtime size on the heap, so, he makes one with unique_ptr but of course by default C++ doesn't remember how big this is, so he needs to make sure he tracks it separately, more anxiety. Rust can Vec::into_boxed_slice() and the resulting slice automatically remembers how big it is, even though you've given up the vector's ability to grow or shrink. The machine representation ends up the same if you do it right in both languages, but C++ gives the programmer another thing to worry about.
Even Google isn't comfortable into pushing Rust into app developers for their OSes, despite the amount of adoption at kernel and driver.
So when Android comes out with Rust Compose, Fuchsia does Rustter, or Native Cloud folks migrate in mass to Rust, then I might eat my hat.
Naturally you might refer to Rust/WinRT, whose tooling is a joke versus C++/WinRT, let alone using .NET alongside Blend and VS.
Doable sure, then again there are people that also insist into using COM from C, regardless of how sane that might look like in practice.
So unless Rust reaches a state where ownership is transparent to app developers and distributed computing stacks, we will keep on disagreeing.
When things get more complicated sure, Rust can mean you spend too much time figuring out how lifetimes should best work compared to a GC environment. But even a fancy GC doesn't promise to release unused resources promptly, so for non-memory resources you can end up needing to care about lifetimes anyway, and the GC languages do not prioritise this problem.
FWIW the phrase you probably wanted is "en masse" from the French meaning "as a body" not in mass.
Exactly to make the point that those languages offer the same deterministic tooling and not mix with how Java does it.
I don't see that as any kind of upside over Rust. It sounds like one of those "my first Rust project" codebases full of Arc<Thing> when Box<Thing>, Thing or sometimes even just &Thing would have been appropriate.
In fact this then makes me realise you consider "it has reference counted smart pointers" to be "automatic memory management" but then you say Rust is suited for situations where that isn't possible, does that mean you didn't know Rust has reference counted smart pointers (two kinds even)? Or does it mean you consider all of Rust above core to be superfluous (Rc is in alloc) ? Either way this makes very little sense.
And so I went back and now I'm also puzzling what you think Value Types are and why you believe that's relevant. Is the problem that you bumped your head and now you think Rust is Java ?
The modern trend is closer to refactoring C++ code so that it's more Rust-like, not the other way around. The C++ Core Guidelines include plenty of indications on "paradigms" and overall design, so this is something C++ developers are being asked to do anyway. But unlike these, Rust is fully intended to ensure no unsafety in the safe subset.
There are a lot of patterns in modern C++ which the borrow checker rejects, for example the observer pattern and dependency injection (the pattern, not the frameworks), which are often the best tool for the situation.
Also, are you saying that the C++ Core Guidelines encourage us to make code more Rust-like? I would love to read up on that, if you have a source.
I don't see any occurrences of "Rust" in this document...
Better like this?
I also assume they meant that that we should adhere to AxM in C++ (since that's the only thing new Rust brings), to make interop with Rust easier, but I could be interpreting incorrectly.
Could you say what they are?
But man, after trying to watch after a C++ project with other kinds of programmers on it I kind of wish it was built into the compiler. Rust is pretty much what I do by convention.
Also when a beginner asks me whether they should start learning C++, prepare for a long uncomfortable silence while I look for the right words.
Rewriting C code in Rust is probably quite economical.
The value proposition of Rust is to make it easier to solve deeper problems by combining a state-of-the-art language (that will be designed to get out of the way as much as possible) with the best efficiency.
> tons of tangled and non-generalizable business logic
This can often be made more maintainable by using embedded DSLs. Rust includes language support for macros that makes it easier to design and use special-case custom languages.