|
|
|
|
|
by detrino
3885 days ago
|
|
Your comment has the structure of a well reasoned argument, but it's predicated on falsehoods. Most large C++ projects do not unnecessarily use reference counting. Most C++ programmers do not fear using references or pointers because of invalidation. The Rust compiler does not tell you when it's impossible to satisfy your problem using borrows. In fact it often has false positives requiring borrow gymnastics because it is too primitive. And your last line is a complete reversal of your previous attitude: Suddenly you are focusing on theory while ignoring that in practice you can not usually break out `unsafe` to solve your borrow problems. In fact it's Rust, not C++, that is more likely to reach for more heavyweight abstractions than necessary because it's not practical to litter unsafe throughout your codebase. |
|
Compared to Rust? I doubt that. Like I said, Rust lets you dance near the line.
This is from experience in various large C++ codebases. I'm not saying people use refcounting a lot, I'm saying it gets used more than Rust.
YMMV though, so it does boil down to a matter of different experiences here. We'll probably have to agree to disagree.
> In fact it often has false positives requiring borrow gymnastics because it is too primitive.
Not really. Aside from non-lexical borrows and a couple other nice-to-have things (but not necessary), the borrow checker is pretty precise for what it tries to prove.
One might argue that the guarantees Rust tries to maintain (one writer or multiple readers for a piece of data) are too primitive. I don't think that's true. Doing a context-sensitive/flow-sensitive analysis might lead to more patterns being allowed but it's hard to scope guarantees when the analysis is interprocedural -- stopping at function boundaries makes sense to me.
> The Rust compiler does not tell you when it's impossible to satisfy your problem using borrows.
It sort of does. If you try to introduce borrows and listen to the suggestions the compiler gives you, and eventually end up nowhere, it's probably not possible to do it that way. It's not perfect, but it's good enough. And it's immune to further changes -- you don't need to design your pointer usage so that it's future-proof; design it however you want, and if a future refactoring introduces a possible use-after-free, fix the compile error.
> And your last line is a complete reversal of your previous attitude: Suddenly you are focusing on theory while ignoring that in practice you can not usually break out `unsafe` to solve your borrow problems.
No, it's not, I'm just pointing out the equivalence. My point was that "The things you can prove statically with the borrow checker are a subset of the things that wont trigger UB in C++." is irrelevant for two reasons -- (a) in practice IMO the things Rust makes you feel safe to do is a superset of what C++ lets you feel safe to do, and (b) if we're going to talk about "all possible things C++ theoretically allows you to do", then you should include unsafe -- you were comparing "safe Rust" with "all C++", which is unfair here, since the entities to be compared for what you're theoretically allowed to do are "all Rust" and "all C++". I focused on a different flaw in your argument, so of course the focus changed. I'm not saying you should use unsafe a lot, I'm saying "what C++ lets you do is equivalent to using unsafe a bit more often in Rust". I don't endorse it, but if you consider "C++ lets you do so many things Rust doesn't" to be a plus point of C++ (I don't), then you should at least be comparing the right things and allowing yourself to use unsafe in Rust too.
> more heavyweight abstractions than necessary
Examples?