AIUI memory safety issues are literally over half of security issues. (Also, Rust's type system makes it much easier to avoid logic bugs).
> C++ isn't inherently unsafe either, you can use it with zero dynamic allocation if you really want or use language tools like ref counted smart pointers.
No you can't. C++ fans always claim this is possible but they're never willing to point at specific rules for which code does or doesn't do this, or which libraries are out there that do or don't follow their rules. It's vaporware.
> Ultimately you can write unsafe and bad rust code too so reviewers will still have to carefully review every submission.
Given that existing review procedures aren't perfect, the acceptable defect rate is clearly not zero. So if switching to Rust eliminates half the bugs, people could do half as much review and the end defect rate would be the same; why would that not be acceptable?
Just go around open source projects from well known ISO C++ members.
I'm no C++ fan, but I've worked on codebases that do zero dynamic allocations including in C++. The rules are something like "never call malloc or use keyword new, communicate with other threads only by copying things into or out of queues." It doesn't save you from buffer overflows or whatever though.
I think the parent's point is that it's less likely a bug will be "lurking". You may disagree with that point too, but I tend to agree that it's more likely a memory safety issue is "lurking" than a logic bug is.
I find doing a code review a demanding task that leaves me drained by the end of it. Despite being drained, I still I don't feel confident I've been thorough enough.
Maybe? It might make it easier for those less experienced with C/C++ memory safety issues to review? Instead of thinking of it as being less strict, I might think of it as -- freeing the reviewer up to focus on other issues.
Here's a recent patch I wrote "Improve E0308: suggest user meant to use byte literal, w/ tests and fix" which adds a suggestion for Rust's diagnostic when you write for example '*' the literal char, Unicode U+002A but it needed a single byte and that's not what a char is. My code suggests adding the prefix b, so writing b'*' here, meaning the ASCII code for that symbol 0x2A which is a single byte ::
https://github.com/tialaramex/rust/commit/130d02b62e65c5f2a4...
I wrote that code but I don't understand the internals of how self.tcx.sess().source_map().span_to_snippet(span) works. Not my problem, we're in the diagnostics code so even if this is perhaps slightly slower than optimal it doesn't matter because a human will need to read this output and act on it - E0308 is a type mismatch, the program does not compile as written.
Does my reviewer know? Maybe, I didn't ask them, but they don't really need to, it's clearly fine here to call this stuff, there won't be a nasty surprise "Oh, make sure you restore the FQ5 when setting Z due to calling sess() in this code" because that's not how Rust works whereas in a language like C++ of course such traps may exist.
Now there is some risk I made a logic mistake, but, I wrote tests for this of course, unlike with subtle memory safety bugs, logic bugs are often caught by proper testing. My tests here are somewhat superficial, I check 'X' and '#' which should both cause the suggestion, and I check '€' which should not, but I think they cover the cases the compiler will really see here.
That is surely a win, no?
"Rust is an emerging programing language that aims at preventing memory-safety bugs without sacrificing much efficiency. The claimed property is very attractive to developers, and many projects start using the language. However, can Rust achieve the memory-safety promise? This paper studies the question by surveying 186 real-world bug reports collected from several origins which contain all existing Rust CVEs (common vulnerability and exposures) of memory-safety issues by 2020-12-31. We manually analyze each bug and extract their culprit patterns. Our analysis result shows that Rust can keep its promise that all memory-safety bugs require unsafe code, and many memory-safety bugs in our dataset are mild soundness issues that only leave a possibility to write memory-safety bugs without unsafe code. Furthermore, we summarize three typical categories of memory-safety bugs, including automatic memory reclaim, unsound function, and unsound generic or trait. While automatic memory claim bugs are related to the side effect of Rust newly-adopted ownership-based resource management scheme, unsound function reveals the essential challenge of Rust development for avoiding unsound code, and unsound generic or trait intensifies the risk of introducing unsoundness. Based on these findings, we propose two promising directions towards improving the security of Rust development, including several best practices of using specific APIs and methods to detect particular bugs involving unsafe code. Our work intends to raise more discussions regarding the memory-safety issues of Rust and facilitate the maturity of the language."
If you do run into memory unsafety in rust, you already have signposts pointing towards possible problems. You get very little help in comparison in C++.
int oops[10];
oops[69] = 420; int x = 0x7fffffff;
x++;
Or: std::array<uint32_t, 2> x = {1, 2};
uint64_t *y = (uint64_t *)&x;
*y;
Or: std::optional<int> x;
*x;
Or: std::variant<std::string_view, int> x;
x = "foo";
auto &y = std::get<std::string_view>(x);
x = 42;
std::cout << y; idiot.c:4:3: warning: array index 69 is past the end of the array (which
contains 10 elements) [-Warray-bounds]
oops[69] = 420;
^ ~~
idiot.c:3:3: note: array 'oops' declared here
int oops[10];
^
But... if 69 is replaced with a user-supplied argument, then that bypasses the detection. int idx = atoi(argv[1]);
int oops[10];
oops[idx] = 420;And so the runtime Panic will occur in Rust only for the dynamic case.
Notice that in WUFFS the user argument code is still a compile time error. WUFFS wants to know why you thought it would be OK to put arbitrary numbers in idx, a variable you are using to index into an array of size 10, and thus which should only have values between 0 and 9 inclusive. It won't be happy until you write logic to ensure this can't happen.
How many many of these were ownership problems that could only be solved with a strict borrow checker and could not be solved with a garbage collector?
GC is unacceptable for some applications. Generally speaking, shifting the memory safety checks to compile time results in better runtime performance. Is the added complexity worth the performance gain? That’s for you to decide.
This is not even close to being true, the borrow checker does not accept all memory safe programs. It tries to do a good job of accepting programs that are both safe and have simple and elegant guarantees of safety across module boundaries (which is good for extensibility and evolvability).
But there are designs that can only be expressed in Rust by using constructs that replace some of these compile-time guarantees with runtime constraints.
Second, this can be tough because it is hard to self-evaluate. The two languages have different semantics. Some stuff is UB in Rust, but well defined in C and C++, and some things that are UB in C and C++ are well defined in Rust.
What this means is, while you are correct that Rust will reject some memory safe programs, I’ve also seen so many people over the years ask questions like “why does the borrow checker disallow this?!? It’s this easy in C++!” yet they’re running afoul of semantic differences, or they forgot some property (like thread safety) that isn’t enforced by the compiler in those languages, but rustc catches it.
Tl;dr you’re technically right but in the real world it plays out more subtly than I think you’re giving it credit for.
> Failing to address these issues creates safety concerns, as users will resort to calling code that expects Rust semantics and create UB by doing so.
Absolutely. What I'm saying is that sometimes this is expressed by users as "I can do this" when they actually can't, which makes it hard to talk about how much the borrow checker truly gets in your way. They're just two different languages with two different sets of semantics, you cannot assume that everything that works well in one works well in the other, in both directions.
Maybe there's a reasonable case that Rust has developed to the point where an experienced Rust user is just as productive as an experienced Java or C# user even when it comes to tasks that aren't performance critical. For my part I think I'd rather use Haskell than Rust for tasks where GC overhead isn't a deal-breaker.
Now imagine if the linter is not optional.
Oh, yeah, that is Rust!
* up to implementation bugs in the compiler
Memory safety bugs, on the other hand, can have unpredictable results that are far away from the code that actually caused the problem. Usually it's possible to figure it out from clues like, "the program only crashes after I use this particular feature" but sometimes memory safety bugs can stick around for a long time without anyone figuring out why every once in a while the program crashes for unexplained reasons.
If someone submits a pull request adding some feature to a big project, then the stakes of accidentally approving a bad change are (usually) a lot less if you're only concerned with logic bugs and they only affect that feature.
Effectively a perfectly interopable (both ways) subset of C++ with some syntactic sugar and niceities.
But yes I agree that best practices of modern C++ means most of the baggage and bad ways of doing things don't have to be done. Now just to enforce it via education and linters or something conventionally.
So I don't really see why Carbon is at all interesting other than it just being another form of their standard "Google being Google" with their extreme "Not Invented Here"-itis. It just sounds like a slightly warmed over version of C++ with different syntax and near identical semantics.
Carbon at first blush appears to show that Google doesn't appear to understand the real problems with C++.
I can't see why anyone other than Google would ever end up using the language, at least as currently described by Google in their goals for the language. In general for something to be rewritten it needs to cause very substantial advantages to justify the rewrite.
If you are in a position where you can wholesale rewrite your program (or are starting greenfield), Carbon is pretty clear upfront that you should use another language: "Existing modern languages already provide an excellent developer experience: Go, Swift, Kotlin, Rust, and many more. Developers that can use one of these existing languages should."
Natively fitting into the C++ ecosystem at Google and in some other extremely large C++ codebases (e.g. being able to directly use templated C++ code, using the same build system, bidirectional interoperability) is what is being prioritized.
But this makes me wonder: is there a language that lacks idiomatic designs because there is only one way to actually do anything? I imagine not but maybe!