[HN Gopher] Safety: A comparaison between Rust, C++ and Go
       ___________________________________________________________________
        
       Safety: A comparaison between Rust, C++ and Go
        
       Author : ephesee
       Score  : 106 points
       Date   : 2022-07-30 07:57 UTC (15 hours ago)
        
 (HTM) web link (nested.substack.com)
 (TXT) w3m dump (nested.substack.com)
        
       | jayp1418 wrote:
       | What about comparing to Ada Programming Language?
        
       | TinkersW wrote:
       | This would be a better article if the UB in the C++ example
       | wasn't blindingly obvious-- no C++ programmer worth a damn would
       | ever write this.
        
         | pfultz2 wrote:
         | Yea and C++ static analysis tools already warn for the case, so
         | even for new C++ programmers where it might not be entirely
         | obvious, its still easy to catch the error.
        
         | ncmncm wrote:
         | More to the point, the compiler warnings would save
         | inexperienced programmers too.
        
         | sagarm wrote:
         | Yeah, agreed that a competent C++ dev would not write the code
         | in the example. The charitable interpretation though is that
         | errors of the same type can crop in real codebases, the example
         | is just simplified for the purposes of discussion.
        
           | pornel wrote:
           | In such examples you're always primed to look for a mistake.
           | It's a different situation when you have 100K+ lines of code,
           | a dozen developers, and a deadline.
           | 
           | It's a difference between "look this is Wally" (duh, of
           | course) and a game of "Where's Wally?".
           | 
           | Whenever a CVE is discussed people say that the error is
           | obvious, and no good programmer would write like that. It's
           | easy in hindsight.
        
         | jrajav wrote:
         | Why is this such a common defense for the peculiarities of C++?
         | I see it pop up at least a couple of times, anytime C++ is
         | criticized.
         | 
         | I don't have anything against taking pride in one's skill and
         | craftsmanship, but excusing a tool's failings purely on the
         | basis that one needs more skill to wield it and avoid those
         | failings? I want to have that same level of skill and have my
         | tool multiply my skill's output to the max, not have my skill
         | wasted coaxing my tool to perform correctly.
         | 
         | If the implication is that the tool requiring more skill gives
         | commensurate benefits, fair enough, but not if it's just hand-
         | waving away obvious downsides.
        
           | TinkersW wrote:
           | Because the code in question isn't something a C++ dev would
           | write, instead it looks like something someone who doesn't
           | use the language much if at all, decided to use for this
           | pretty silly comparison.
           | 
           | It would be one thing if this required skill, but this
           | example is downright silly. Some other people here have
           | posted more reasonable version, that might actually occur in
           | the real world(like the example with std::span)
        
       | vitus wrote:
       | I feel like a better comparison would be to use std::span (C++20)
       | to mimic Rust's slice. Otherwise you might be tricked into
       | thinking that adding                   // hey don't pass in a
       | temporary         auto make_appender(std::vector<int>&& suffix) =
       | delete;
       | 
       | (which turns the provided code into a compiler error under both
       | g++ and clang++) is adequate to prevent the immediate class of
       | issues (namely, C++ allows const Foo& and Foo&& to bind to
       | temporaries).
       | 
       | Meanwhile, it's really really easy to make std::span dangle:
       | #include <cassert>         #include <span>         #include
       | <utility>         #include <vector>
       | std::vector<int> append(std::vector<int>&& items, std::span<int>
       | suffix) {           items.insert(items.end(), suffix.begin(),
       | suffix.end());           return items;         }
       | auto make_appender(std::span<int> suffix) {           return
       | [=](std::vector<int>&& items) {             return
       | append(std::move(items), suffix);           };         }
       | auto make_appender34() {           std::vector<int> vec = {3, 4};
       | return make_appender(vec);         }                  int main()
       | {           auto append34 = make_appender34();
       | assert((std::vector<int>{1, 2, 3, 4} == append34({1, 2})));
       | }
        
         | ncmncm wrote:
         | That would be another tendentious example. Nobody would write a
         | make_appender that takes a span argument, because it makes no
         | sense.
        
       | niekb wrote:
       | The author could compile c++ with the sanitizers, i.e.
       | -fsanitize=address,undefined and make a make_appender function
       | that leverages perfect forwarding...:
       | template<typename S>       auto make_appender(S&& suffix)       {
       | return [perf_fwd_suffix =
       | std::tuple{std::forward<S>(suffix)}](std::vector<int>&& items)
       | {             return append(std::move(items),
       | std::get<0>(perf_fwd_suffix));         };       }
       | 
       | see: https://godbolt.org/z/M9P4MK4a8
        
       | ncmncm wrote:
       | Disingenuous.
       | 
       | The bad C++ code is in the very first line of the "make_appender"
       | definition: capturing the closure's environment by reference is
       | nonsense: It is equivalent to returning a reference to an
       | argument. It is not, then, a closure at all.
       | 
       | Using a correctly-defined make_appender would not, then, produce
       | undefined behavior when you use it, with or without "move".
       | 
       | What the author has done here was to take a too-obviously wrong
       | operation, returning a reference to an argument, but dress it up
       | with syntax that will look less familiar to some readers, and
       | pass it off as insightful.
       | 
       | But using a wrong function and getting wrong results is not
       | surprising.
       | 
       | Piling on more uses after, that give more wrong results, does not
       | reveal anything more.
       | 
       | When you need disingenuous arguments to make your point, it tells
       | us more about your point than about the thing you are trying to
       | make a point about. And, publishing anyway tells us more about
       | you.
       | 
       | Returning that fake closure should evoke a compiler warning, if
       | you turn on warnings.
        
         | dataflow wrote:
         | You can add [[clang::lifetimebound]] on the suffix parameter
         | and you get                 <source>:21:35: warning: temporary
         | whose address is used as value of local variable 'append34'
         | will be destroyed at the end of the full-expression
         | [-Wdangling]         auto append34 = make_appender({3, 4});
        
           | pfultz2 wrote:
           | You dont need annotations, cppcheck already warns with:
           | test.cpp:16:45: error: Using object that is a temporary.
           | [danglingTemporaryLifetime]
           | assert((std::vector<int>{1, 2, 3, 4} == append34({1, 2})));
           | // FAIL: UB
           | ^         test.cpp:3:12: note: Return lambda.
           | return [&](std::vector<int>&& items) {                    ^
           | test.cpp:2:50: note: Passed to reference.         auto
           | make_appender(std::vector<int> const& suffix) {
           | ^         test.cpp:4:36: note: Lambda captures variable by
           | reference here.                 return append(move(items),
           | suffix);                                            ^
           | test.cpp:15:35: note: Passed to 'make_appender'.
           | auto append34 = make_appender({3, 4});
           | ^         test.cpp:15:35: note: Temporary created here.
           | auto append34 = make_appender({3, 4});
           | ^         test.cpp:16:45: note: Using object that is a
           | temporary.             assert((std::vector<int>{1, 2, 3, 4}
           | == append34({1, 2}))); // FAIL: UB
        
             | dataflow wrote:
             | As awesome as that is, cppcheck doesn't seem ready for
             | real-world use. Literally the first invocation I ran
             | resulted in this error:                 error: Syntax
             | Error: AST broken, binary operator '!=' doesn't have two
             | operands. [internalAstError]        explicit operator
             | bool() const { return this->get() != pointer(); }
             | 
             | This is for a simple wrapper class that looks like this:
             | template<class T>       class Foo : private Bar<T> {
             | public:        typedef value_type *pointer;        pointer
             | get() const { return ...; }        explicit operator bool()
             | const { return this->get() != pointer(); }       };
             | 
             | Another example:                 void foo(uintptr_t const
             | (&input)[2]) {        if constexpr (sizeof(uintptr_t) ==
             | sizeof(int) && sizeof(long long) == 2 * sizeof(int)) {
             | long long value;         memcpy(&reinterpret_cast<uintptr_t
             | *>(&value)[0], &input[0], sizeof(input[0]));
             | memcpy(&reinterpret_cast<uintptr_t *>(&value)[1],
             | &input[1], sizeof(input[1]));        }       }
             | error: The address of local variable 'value' is accessed at
             | non-zero index. [objectIndex]
             | memcpy(&reinterpret_cast<uintptr_t *>(&value)[1],
             | &input[1], sizeof(input[1]));
             | ^
        
         | dannymi wrote:
         | >The bad C++ code is in the very first line of the
         | "make_appender" definition: capturing the closure's environment
         | by reference is nonsense: It is equivalent to returning a
         | reference to an argument.
         | 
         | If it is so bad, it should (in the sense of how things would be
         | in an ideal world) not compile.
         | 
         | >It is not, then, a closure at all.
         | 
         | It is a closure because all variables are closed-over and there
         | are no free variables in the lambda body anymore. That is the
         | definition of closure.
         | 
         | >But using a wrong function and getting wrong results is not
         | surprising.
         | 
         | In an ideal world, there should be a compilation error. (There
         | is in Rust)
         | 
         | The majority of what's wrong in C++ is that it lets you do
         | nonsensical (even dangerous) things, most of the time without
         | even a warning (and not because it's technically impossible to
         | warn--it just didn't occur to them). It's okay to acknowledge
         | that--it's a product of its time.
         | 
         | >Returning that fake closure should evoke a compiler warning,
         | if you turn on warnings.
         | 
         | That "should" tells me all I need to know. In the end either
         | safety is important, or it isn't. Choose accordingly.
        
           | keithalewis wrote:
           | "Doctor, it hurts when I do that!"
        
             | wruza wrote:
             | Pain is a strong warning that prevents [further] trauma.
             | This analogy defeats its own purpose.
        
           | jstimpfle wrote:
           | >> In the end either safety is either important, or it isn't.
           | Choose accordingly.
           | 
           | Not to disagree with your post but this comes across as very
           | black-and-white.
        
             | dannymi wrote:
             | To expand:
             | 
             | There's a prioritized list of requirements in engineering.
             | Use the right tool for the job.
             | 
             | Rust has: (see https://hackmd.io/@rust-ctcft/r1plN4You#/5 )
             | 
             | 1. Safety
             | 
             | If you don't need that at the first slot, then the biggest
             | strength of Rust doesn't apply to your problem (and you
             | would waste time having to track lifetime parameters more
             | than necessary to solve your problem).
        
               | pdimitar wrote:
               | Big problem in these discussions is what should be
               | prioritized.
               | 
               | To me, with the 20 years of experience I have in 8
               | programming languages, I'll always include safety.
               | Especially having in mind that it's nearly zero-cost in
               | terms of runtime performance.
               | 
               | So to me not choosing safety is a strong sign that I
               | don't want to work with the people who practice that.
        
               | estebank wrote:
               | There's also another item in that list, and that is
               | performance. As soon as you slightly relax that one, Rust
               | does become massively easier. A well placed .clone() or
               | Arc makes the rest of the code performant enough and
               | easier to understand, which makes the equation of
               | choosing Rust over other alternatives in spaces that
               | aren't necessarily systems programming less problematic.
        
           | unsafecast wrote:
           | > If it is so bad, it should (in the sense of how things
           | would be in an ideal world) not compile.
           | 
           | Yes, C++ can be a bad solution to a lot of problems, and
           | that's okay. Use rust if you need a machine guarantee for
           | memory safety (or you just like the language), you can use Go
           | if you just don't care about that at all and want the
           | language to take care of it. But you can use C++ for non-
           | critical software that needs to be fast (games come to mind).
           | Rust can be too much of a mental overhead than it's worth for
           | some.
           | 
           | That closure is just not a good example. Nobody would write
           | this, because when writing C++ code you _do_ think about
           | whether you want a reference or not. Sure, a lot of bugs can
           | happen but this is not, in my opinion, one of them.
           | 
           | If you want to prove a point, prove it fairly.
           | 
           | Note: I don't use C++ anymore, and I don't like it very much
           | for other reasons.
        
             | fjdbeb76 wrote:
             | > Rust can be too much of a mental overhead than it's worth
             | for some.
             | 
             | Rust has the least mental overhead of any language I've
             | ever used. The compiler literally takes all the mental
             | overhead away.
        
         | CrendKing wrote:
         | The fact that you may know that make_appender definition being
         | bad does not mean every C++ user knows as well. It's also
         | impossible for you to know ALL the possible bad, UB leading C++
         | code out there.
         | 
         | I think the point the author tries to make is that, while C++
         | and Rust are probably "the same" for the most skillful and
         | disciplined programmers (such as you), for average human, Rust
         | just catches way more errors they make for them early. An
         | extreme analogy would be trying to climb Everest all by
         | yourself vs with a professional guide.
        
           | ncmncm wrote:
           | Literally anybody can turn on warnings and heed the results.
        
           | benreesman wrote:
           | Not to disagree necessarily, but if you (give me a little
           | rope) bucket languages that are hard to get past the compiler
           | but more often correct when you do (Rust, Haskell), and
           | languages where it's pretty easy to get _something_ past the
           | compiler and tweak it until it works well enough for your
           | purposes (JS, C /C++), the tweak-it-until-it-kinda-works
           | languages are _fucking killing it_ on adoption.
        
             | gmnash wrote:
             | JS and C++ are killing it on adoption because they had near
             | monopolies for an extended period of time in their
             | respective areas (browser, native higher level language).
             | They are popular despite their obvious (some in hindsight)
             | shortcomings due to lack of alternatives in the same
             | categories.
        
             | zozbot234 wrote:
             | > it's pretty easy to get _something_ past the compiler and
             | tweak it until it works
             | 
             | That's just as true of Rust if you use clone(), Rc<> and
             | RefCell<>. You just have to familiarize with a few
             | boilerplate patterns, and the best part is you're only
             | trading off a modicum of performance while preserving
             | safety. But Rust can work quite well as a language for
             | exploratory programming.
        
             | grumpyprole wrote:
             | > the tweak-it-until-it-kinda-works languages are fucking
             | killing it on adoption.
             | 
             | Industry clearly prioritises speed of delivery above all
             | else. Security, reliability and maintenance are future
             | problems (that would be nice to have).
             | 
             | However, in order to gain the power to reason about our
             | code and be able to prove the correctness of various
             | properties (what a typechecker does), we have to program
             | with more restrictive models. This has been argued many
             | times before (e.g. Dijkstra's structured programming).
             | Haskell and Rust are just two of many examples. My
             | favourite is regular expressions, choose actual proper
             | regexes and you have guaranteed O(n) execution, choose
             | Perl/Python "Regex" and you have a potential security hole.
        
             | davedx wrote:
             | I dunno man. I've done C, C++, JavaScript and TypeScript
             | professionally for significant chunks of my career, and the
             | trend that I've observed has overwhelmingly been towards
             | stricter compilers. For example in the front-end world,
             | TypeScript has absolutely exploded in adoption. Everyone
             | could be still using JavaScript, but companies from
             | startups to huge corporates have explicitly decided they
             | want compile type safety -- often in "only" front-end code.
             | 
             | I suspect that the adoption of Rust as a system language is
             | going slower because that's just the natural pace of
             | embedded/systems development, not because of anything
             | intrinsic to Rust. There are now 30k+ lines of Rust code
             | [1] in the Linux kernel. C++ can't claim that.
             | 
             | [1] https://www.phoronix.com/news/Rust-v6-For-Linux-Kernel
        
               | benreesman wrote:
               | Oh I think we probably see largely eye-to-eye. My weapon
               | of choice when there are no other constraints is Haskell,
               | and one reason I really like Rust is that I can get a lot
               | of the Haskell features I like in a highly-performant
               | setting. Most of the C++ I maintain these days is in
               | whole or in part generated by Haskell. And if I have to
               | write something fast by hand and it doesn't need to link
               | to stuff I need, I reach for Rust generally.
               | 
               | I was also unaware that Rust has such significant
               | penetration in the Linux kernel, and that's a place where
               | I can see it really shining.
               | 
               | My first comment in this thread was something to the
               | effect that Rust has _tons of great stuff to offer_ , and
               | that the memory safety argument is actually weaker than
               | people think and probably not the only thing people
               | should talk about.
               | 
               | The resulting gang-tackle is just one more data point
               | that the community is still too small and evangelical for
               | me to want to get involved past my proprietary Rust
               | stuff.
        
               | davedx wrote:
               | Ha, Rust community is very energetic, but IMHO they
               | largely put that energy to good use!
               | 
               | I'm using it for a new project and honestly I'm using it
               | more for the modern tooling and easy C interop than the
               | safety features, but I'm a fan overall. Think it's a
               | really good language.
        
               | unsafecast wrote:
               | > Most of the C++ I maintain these days is in whole or in
               | part generated by Haskell
               | 
               | Can you expand on that? I'm currently researching
               | something similar but lower level & lisp instead of
               | Haskell. It would help to see some existing examples to
               | figure out if it's worth it or not.
        
               | benreesman wrote:
               | Ideally we'll just be able to open source it soon!
               | 
               | Basically we have a nice Haskell DSL for generating
               | arbitrary C++, and we deal with lots of code you wouldn't
               | want to write by hand (big nested switch statements and
               | other kinds of state-machine logic, choices about loop
               | unrolling, lots of template overloads, SIMD intrinsics
               | that require immediate values, etc. etc.) so we write
               | Haskell that generates C++ and feeds it to e.g. `clang`.
               | 
               | Some of this is directly in Haskell, and some of it is
               | little compilers mostly done using Megaparsec. It's a
               | really nice approach where it fits!
        
               | unsafecast wrote:
               | Sounds great, that's what I was imagining too. Is there a
               | way I can find it when/if you open the source?
        
             | bhaak wrote:
             | Javascript's adoption has much less to do with its language
             | features but with its unique positioning.
             | 
             | There was (and still isn't) a competitor that can compete
             | on the same level with Javascript in the browser.
        
         | [deleted]
        
         | benreesman wrote:
         | I was trying to be a little more diplomatic in my sibling reply
         | because I've locked horns with the Rust community before and
         | not enjoyed it, but you're not wrong.
        
         | jstimpfle wrote:
         | std::vector<int> suffix{3, 4};         auto append34 =
         | make_appender(suffix);  // Version 1: test will work
         | auto append34 = make_appender({3, 4});  // Version 2: test will
         | fail
         | 
         | I was in a mood to type the examples but I accidentally made
         | Version 1 because I had started with the author's first
         | example. I wondered where the problem was and couldn't find any
         | (neither running nor reading the code) until I noticed the
         | author had changed to Version 2.
         | 
         | The problem is "obvious" in hindsight but one of the problems
         | with C++ is that it makes certain things too
         | implicit/convenient. I get that references are a staple feature
         | in C++ but it's not at all obvious from the calling location
         | that Version 2 is a bug. To see that, you have to navigate to
         | the callee (finding which might be hard enough alone without a
         | very solid IDE), and make sure that it's not capturing the
         | argument by reference or not doing anything unsafe there.
         | 
         | It's often a problem when a seemingly "value" argument is
         | turned into actually a pointer-to argument at the callee. There
         | are other languages that have this too, and I've never liked
         | it.
         | 
         | As someone who doesn't regularly code in C++ but has a solid
         | understanding of the basics, I wonder why C++ ever allowed to
         | have a reference parameter be called with a temporary? To me it
         | feels like "References have value syntax but pointer semantics
         | BUT you should program like it had value semantics"? Which to
         | me would be exactly a premature optimization that is looking
         | for trouble.
        
           | benreesman wrote:
           | Again, this isn't a right/wrong thing. Rust moves by default
           | (and a lot of people find "=" a weird pun for that), C++
           | copies by default and has rvalue-references and an explicit
           | `std::move`.
           | 
           | If you want copy in your C++ lambda, you start it `[=](...)
           | {...}`, if you want take a pointer in your C++ lambda, you
           | start it `[&](...) {...}`, if you want something trickier you
           | do trickier stuff.
           | 
           | Rust opts you in to the nitpicky static analyzer and you have
           | to opt out with `unsafe`, in C++ you have to opt in with e.g.
           | `clang-tidy` or some annotations.
           | 
           | They are remarkably similar, just with different defaults.
        
             | pjmlp wrote:
             | There lies the problem with C++ state of affairs, most
             | defaults are wrong.
             | 
             | It doesn't help that those of us that care about enabling
             | the right defaults are a tiny minority as per C++ surveys.
             | 
             | https://blog.jetbrains.com/clion/2021/07/cpp-ecosystem-
             | in-20...
        
             | tialaramex wrote:
             | > They are remarkably similar, just with different
             | defaults.
             | 
             | I'm going to lead with this, because I think it's most
             | important: Culturally there's a world of difference. Safety
             | is a part of Rust's _culture_.  "Culture eats strategy for
             | breakfast".
             | 
             | Take sorting. In C++ the default sort is unstable, while in
             | Rust the default sort is stable, that's just those defaults
             | you mentioned (each has both kinds), although the choice
             | speaks to culture. But look closer, in C++ the sort has
             | _undefined behaviour_ if your type isn 't totally ordered.
             | In Rust you can't sort the partially ordered types without
             | saying how to order them fully. Still, in both languages we
             | can write a custom order, so what happens then? In C++ if
             | your custom order is nonsense you get... undefined
             | behaviour. In Rust sorting won't necessarily _work_ with a
             | nonsense custom order but the behaviour remains well
             | defined.
             | 
             | > Rust opts you in to the nitpicky static analyzer and you
             | have to opt out with `unsafe`
             | 
             | Unsafe gives you a small number of dangerous "super powers"
             | needed to write efficient low-level code, it does _not_ opt
             | out of the borrow checker 's analysis, or indeed most other
             | checks. This misconception makes me wonder how much of what
             | you've written is conjecture rather than practical
             | experience.
        
               | benreesman wrote:
               | The sibling says that C++ has the _wrong_ defaults, full
               | stop.
               | 
               | Well in Rust the default `HashMap` uses a cryptographic
               | hash, and you see it _everywhere_ , it's the de facto
               | community "default". In C++ the community "default" is
               | `absl::flat_hash_map`/`folly::F14`, which use SIMD to
               | compare a whole stripe of key-prefixes simultaneously.
               | 
               | I want different defaults for different programs, but the
               | idea that it's esoteric to ever want an associative
               | container within arms reach that _fucking demolishes_ the
               | other one is, ugh, God I want to like Rust even more than
               | I do but this  "we're right and everyone else hasn't seen
               | the light" routine is infuriating and pushes me at least
               | away.
               | 
               | My parent comment is trying to emphasize that this isn't
               | a right/wrong thing, different tools for different jobs.
               | And people are just like: "nope, everything but Rust is
               | _wrong_ ".
        
               | jjnoakes wrote:
               | Why do you think that a language with better details
               | would prevent you from opting in to whatever non-default
               | behavior you need?
               | 
               | Having the "right" defaults is better for everyone. Folks
               | who don't know or care get a good, safe default with no
               | undefined behavior or unexpected danger, and folks who
               | know better can opt into something that fits their needs
               | explicitly.
               | 
               | Seems ideal to me.
        
               | jjnoakes wrote:
               | > a language with better details
               | 
               | Whoops - that should have said "a language with better
               | defaults"...
        
               | ncmncm wrote:
               | It might be more reasonable to default to a crypto hash,
               | but in fact it _also_ does not actually improve security.
               | 
               | So, the trade is just: slow for nothing.
        
               | mattgreenrocks wrote:
               | I like and use both C++ and Rust. I also have plenty of
               | bones to pick with both languages.
               | 
               | However, I've never gotten the sense that Rust itself
               | promotes the idea that "everything except for Rust is
               | wrong." I also don't read much on the Internet these
               | days, and I'm not doing so, probably avoid much of the
               | hype that people are pushing about Rust.
               | 
               | Since it has been established as Hot New Thing, there are
               | huge social incentives tied up in promoting it.
        
               | CryZe wrote:
               | > In C++ the community "default" is
               | `absl::flat_hash_map`/`folly::F14`, which use SIMD to
               | compare a whole stripe of key-prefixes simultaneously.
               | 
               | Just to be clear, the Rust HashMap does the same thing.
        
               | benreesman wrote:
               | TIL. Thanks for letting me know that: https://doc.rust-
               | lang.org/src/std/collections/hash/map.rs.ht...
               | 
               | I don't know how I missed this, is the Swiss port a
               | fairly recent development?
        
               | tialaramex wrote:
               | Hashbrown (the Swiss Tables implementation) replaced the
               | previous HashMap implementation in July 2019.
               | 
               | The port is a little older, 2018. The idea was famously
               | explained at CppCon 2017, I don't know whether Google had
               | published on Swiss Tables before that year.
        
           | snovv_crash wrote:
           | You can also 'const' your std::vector when you declare it and
           | it won't work. Any linter or having warnings turned on will
           | catch these issues.
        
             | jstimpfle wrote:
             | const doesn't make a difference in this case. It's about
             | the passed-by-reference object being destroyed after the
             | function returns. That is because the object was passed as
             | a temporary.
        
         | crabbygrabby wrote:
         | It's possible the author actually made this mistake, and
         | because c++ is C++ they didn't realize why/how. Not a lot of
         | programmers have a good handle on c++, maybe even most don't...
         | Hence these other languages.
        
           | ncmncm wrote:
           | If the author deliberately chose to compile with warnings
           | turned off, in order to present an example that would crash,
           | then that tells us more about the author than about the
           | point.
        
         | [deleted]
        
         | roca wrote:
         | A function returning a value that depends on the lifetime of
         | the function's parameter is not crazy at all. Every class
         | getter method that returns a reference to a member of the class
         | does this.
        
           | benreesman wrote:
           | Sure. But returning the address of a stack-allocated object
           | is (usually) broken.
           | 
           | There isn't a right and wrong here: sometimes you want to opt
           | in to the check for that, sometimes you want to opt out of
           | it.
           | 
           | Sometimes I _actually do_ want to fuck with addresses on the
           | stack in weird, potentially architecture-dependent ways, it
           | 's rare but it happens.
           | 
           | I happen to think that Rust's linear/affine typing is by far
           | the most usable low/zero-cost memory management model that
           | anyone has demonstrated at scale and a real achievement in
           | practical computer science, but it comes at a pretty serious
           | cost in `Box`-this and `Arc`-that and `Rc`-other-thing and
           | generally the borrow-checker being a PITA about some stuff
           | we're used to doing.
           | 
           | Rust is very cool and I use it, but the "using C/C++ is
           | fucking strangers without protection"-vibe got old _years_
           | ago.
        
             | tialaramex wrote:
             | > returning the address of a stack-allocated object is
             | (usually) broken.
             | 
             | What matters is the lifetime, where the object lives is a
             | rule of thumb for guessing lifetime that results from C++
             | trauma.
        
       | DmitryOlshansky wrote:
       | The fact that the GC "owns" all of the memory happens to help a
       | lot with lock-free stuff.
        
       | alrlroipsp wrote:
       | Screenshot absolutely make me *PZs%0>>,-
        
       | phendrenad2 wrote:
       | Can we throw Python in too? Python has more safety than any of
       | these languages, yet it's always left out of these discussions.
        
         | pdimitar wrote:
         | You're joking, right? A dynamically-typed language has more
         | safety?...
        
         | aliqot wrote:
         | Is this satire?
        
       | benreesman wrote:
       | Rust has a lot of great qualities that C++ lacks, but comparing
       | `rustc` to `gcc` or `clang` on move-semantics checking is just
       | kind of silly these days. `rustc` has `clang-tidy` built in.
       | 
       | `clang-tidy` is not letting you mutate or even access that moved-
       | from "suffix" object without throwing an error.
       | 
       | It's annoying that you need `clang-tidy` and ASAN and shit to get
       | comparable runtime safety even in greenfield C++, but that's not
       | why to prefer Rust.
       | 
       | Prefer Rust because of a better traits system and pattern
       | matching and syntax for `Maybe/Either` and a consistent
       | build/library story and a number of other things.
        
         | blub wrote:
         | Objectively speaking, Rust does not "have a lot of great
         | qualities that C++ lacks". Any new feature or improvement has
         | its pluses, but also its minuses.
         | 
         | * Rust has traits, but does not support OOP. Architectures
         | where OOP is particularly effective are proving to be a
         | significant challenge for Rust - GUIs are the obvious one, but
         | also game development Rust projects have to invent new
         | approaches.
         | 
         | * Option/Result make the code flow obvious, but having to
         | return them in nearly all function calls is tedious. Rust has
         | had several attempts at alleviating this problem, but still
         | hasn't matched the convenience of exceptions.
         | 
         | * match is IMO syntactic sugar and its benefits for correctness
         | are being oversold. The fact that one is basically obliged to
         | use it leads to code that's sometimes too deep. Exceptions
         | would cut through this error-handling noise, if they were
         | available.
         | 
         | * The build story is convenient, but this had the unintended
         | effect of encouraging dependency explosion. Adding typical
         | crates results in dozens of transitive dependencies being
         | included in a project.
         | 
         | The notable improvement that Rust brings to the table is
         | machine-verified memory safety with C++-like performance, but
         | that of course comes at the cost of having to adapt code to
         | what the borrow checker understands. If one _needs_ the
         | feature, then the cost is worth paying, if not, Rust is more of
         | a personal choice than an inevitable conclusion.
         | 
         | And finally, perhaps Rust's biggest sin is that it's big, it's
         | complex and there's no end in sight to the complexity spiral,
         | just like for C++. I can only imagine the chagrin of the Rust
         | community as they see themselves competing with Go for many
         | projects where performance is not absolutely critical and often
         | being second choice exactly because of this complexity.
        
           | ncmncm wrote:
           | More to the point, Rust _lacks key features_ I need to
           | capture essential semantics into libraries. So, the libraries
           | I could write in Rust would be less powerful than libraries I
           | can write in C++.
           | 
           |  _Among_ common uses for these more powerful features is to
           | make misuse of the library into a compile-time error. Coding
           | the library in Rust, if possible at all, would mean failing
           | to prevent these usage errors.
           | 
           | The point here is that C++ puts more power in the hands of a
           | library writer, and both the responsibility and capability to
           | enforce safety in _uses_ of the library. Rust jealously
           | reserves maintaining safety to the compiler alone.
        
         | blakehawkins wrote:
         | And way, way better compiler errors thanks to a sane generics
         | system
        
           | bbojan wrote:
           | I think you meant thanks to a sane(r) macro system? Both Rust
           | and C++ use monomorphisation for generics, I believe shitty
           | compiler errors are due to C++'s templating.
        
             | throwaway17_17 wrote:
             | I'm what way can you have 'generics' in C++ that are not
             | based on templating? I am almost certain that any
             | implementation of anything 'generic' templates are
             | inherently involved. Maybe I'm wrong about what you mean by
             | generics though.
        
               | estebank wrote:
               | There are concepts now which are close enough to Rust's
               | traits.
        
           | benreesman wrote:
           | And much better compiler errors. `rustc` about leads the pack
           | on error messages of all the languages I use regularly.
        
         | [deleted]
        
         | estebank wrote:
         | Your point is a fair one, but defaults matter. There's little
         | reason for clang-tidy not to be part of the default clang
         | invocation by now, other than an aversion to producing new
         | output for existing projects (that you could argue are already
         | "broken"). Unless clang-tidy has false positives, in which case
         | the comparison isn't apples to apples then.
        
           | benreesman wrote:
           | Oh I completely agree that defaults matter, and I wouldn't
           | advise anyone to go start some big C++ project unless they
           | had a very good reason to.
           | 
           | But Rust vs. C++ is not an apples-to-apples comparison in
           | this regard: Rust got a clean slate and was willing to cut
           | ties with engineer-millenia of existing high-value software
           | to do it.
           | 
           | In a perfect world Rust wouldn't be ambivalent at best and
           | hostile at worst to C++ interop, c'est la vie.
        
             | estebank wrote:
             | Rust isn't hostile about C++ interop, it's that between
             | native interop (which requires dealing with templates and
             | memory unsafety) and safety, Rust prioritized safety, while
             | Carbon is trying the alternate approach. The behavior of
             | C++ is simply hard to interface with while providing the
             | assurances Rust gives you.
             | 
             | Edit: how many languages have native C++ interop that
             | supports the whole language? Would love to hear of any.
        
               | benreesman wrote:
               | I said ambivalent to hostile at worst, and I'm sorry, it
               | is. I write FFI to C++ in Rust, Python, and Haskell
               | practically every week, and Rust <-> C++ sucks.
               | 
               | Slap it in an `unsafe` block, fine. But let me move a
               | `std::vector<std::string>` into my `unsafe` block easily.
               | Erase the types, fine. But let me call `v.at(idx)`.
               | 
               | Python takes C++ interop seriously, which is why
               | Tensorflow and PyTorch and all the other people trying to
               | script gigantic, extreme-value C++ codebases use it. Try
               | `pybind11` sometime, it's night and day.
               | 
               | Edit to reply to edit: `pybind11` supports an _absurd_
               | amount of C++ out of the box with completely natural
               | semantics and a very modest performance penalty. So,
               | Python.
        
               | touisteur wrote:
               | I wouldn't say python takes C++ interop seriously, it's
               | more like the pybind11 people are amazing at what they do
               | and found a way to slice the problem neatly. But yes,
               | it's night and day, pybind11 is a godsend.
        
               | estebank wrote:
               | Thanks, I'll look at pybind11. Have you looked at cxx by
               | any chance?
        
               | benreesman wrote:
               | I have. `cxx` and `autocxx` and `bindgen` and `cbindgen`
               | are, better than nothing I guess? But they're all flakey
               | and have weird corner cases (and crash sometimes! I'm
               | looking at you `cbindgen`!) and don't handle containers
               | well if at all and just, ugh.
               | 
               | I always end up saying fuck it and `extern "C"`-ing
               | everything. It would be completely possible to make these
               | tools work well, but the Rust ethos is "rewrite
               | everything, pure Rust", at least in large parts of the
               | community, and so these projects kind of never get
               | totally dialed in.
        
               | zozbot234 wrote:
               | > But they're all flakey and have weird corner cases and
               | crash sometimes!
               | 
               | Did you report those crashes? There's also crubit these
               | days, mostly combining the autocxx and (c)bindgen
               | approaches. (It's a very new effort, which is why it's
               | being kept separate from these more established ones.)
        
               | dureuill wrote:
               | was not aware of crubit, so thanks for posting. i'm
               | trying to follow closely what's happening in this space
        
               | infamouscow wrote:
               | It's not just C++ interop. We chose Zig because writing C
               | library bindings for Rust is bewildering.
        
             | TillE wrote:
             | Given the vast C++ ecosystem (including pretty much every
             | big game engine), there _are_ still a ton of very good
             | reasons to start a new big C++ project.
             | 
             | At least until Carbon is ready! But that's a few years away
             | at the least.
        
           | pjmlp wrote:
           | They do, that is why any C++ shop where code quality is
           | relevant has a DevOps team that cares about the right
           | defaults being enforced on th CI/CD pipeline.
           | 
           | Devs that don't care only get to build on their own
           | computers.
           | 
           | It isn't fullproof, but definitely helps to tame some
           | cowboys.
        
           | dataflow wrote:
           | > Unless clang-tidy has false positives, in which case the
           | comparison isn't apples to apples then.
           | 
           | Confused... so you're suggesting Rust's checks are somehow
           | free of false positives? Doesn't the halting problem get in
           | the way? One Rust-specific example: https://www.reddit.com/r/
           | rust/comments/nr7a33/is_the_borrow_...
        
             | estebank wrote:
             | There's a difference between code that used to compile no
             | longer compiling because of an incorrect lint, and code
             | that was never accepted. Rust is restrictive and gets less
             | so over time. C and C++ _need_ to become more restrictive
             | over time, but that 's a more traumatic direction.
        
               | dataflow wrote:
               | What you're actually arguing seems to be "why I like Rust
               | more than C++", not arguing why "clang-tidy has false
               | positives, and thus the comparison isn't apples to apples
               | then".
               | 
               | Clearly the positives can be just as false in Rust as in
               | C++. Your actual objection is that anyone arguing that
               | any feature of Clang can measure up to the corresponding
               | feature of Rust _at present_ is automatically
               | disqualified from making that argument because... Clang
               | 's past "taints" its present? Like an original sin of
               | sorts, but in programming? ("Apple" forbidden against
               | comparison?)
        
               | estebank wrote:
               | It's not about an original sin, it's about the
               | practicalities of changing the default behaviour without
               | pissing off your users.
        
               | dataflow wrote:
               | Sure, but again, that's an argument for why you dislike
               | C++, not an argument for why false positives in that
               | Clang-Tidy check somehow disqualify it from being
               | compared to the corresponding checks in Rust, especially
               | when they both have false positives.
        
               | ufmace wrote:
               | I don't think it's about being tainted. It's that there's
               | a bunch of C++ code out there already in Production in a
               | ton of places that doesn't pass those checks, and may or
               | may not actually be safe. It would be a ton of work that
               | may not produce any end-user value for any C++ project to
               | switch over to that.
               | 
               | Meanwhile, Rust has always had those checks, so there
               | can't be any Rust code in Production that doesn't pass
               | them that would be painful to switch over.
        
             | benreesman wrote:
             | I find it so weird that the Rust community is borderline
             | evangelical about memory safety when a) it's not actually
             | memory safe once you start doing heavy shit b) modern C++
             | is quite memory safe and c) there are so many other great
             | reasons to like Rust.
             | 
             | Memory safety in serious systems software is something that
             | you approach asymptotically and/or probabilistically. Rust
             | makes it _easier_ to be memory safe in a lot of scenarios,
             | at the cost of the father-knows-best borrow checker, but a
             | crashed program is a crashed program whether I dereferenced
             | a null pointer or was poking around in a slice with multi-
             | byte Unicode characters in it. And that 's before you get
             | to `rg unsafe` on your favorite industrial-strength Rust
             | codebase.
             | 
             | Rust is cool for so many great reasons that get talked
             | about so little because everyone seems too busy acting
             | superior about memory safety. Talk about traits! Or Cargo!
             | Or the cool async stuff! Anything but another lecture on
             | memory safety.
        
               | dannymi wrote:
               | >a crashed program is a crashed program whether I
               | dereferenced a null pointer or was poking around in a
               | slice with multi-byte Unicode characters in it
               | 
               | Most of the biggest advances in software engineering are
               | because of increased modularity. One of the best
               | traditional ways to increase modularity is the ability to
               | define and call functions. But any isolation between
               | these "function" modules is only possible if you can at
               | least factor out things into a function mechanically
               | without introducing crashes (for example because of
               | memory unsafety--modularity would fly out of the window
               | right there).
               | 
               | >Rust is cool for so many great reasons that get talked
               | about so little because everyone seems too busy acting
               | superior about memory safety. Talk about traits! Or
               | Cargo! Or the cool async stuff! Anything but another
               | lecture on memory safety.
               | 
               | It's better not to dilute the message. All these other
               | things are nice-to-have gimmicks. But the memory safety
               | is a game-changer. It does no good to advertise 230
               | features at the same time. No one will remember.
               | Advertise the killer feature. And that's the lifetime
               | stuff, which gives you memory AND THREAD safety.
        
               | pjmlp wrote:
               | Rust's thread safety only applies to the special case of
               | those threads accessing in process data segments.
               | 
               | Rust's type system can do very little to help when those
               | threads are accessing the same record on a database
               | without transactions, OS IPC on shared memory,
               | manipulating files without locks, handling hardware
               | signals, handling duplicate RPC calls,...
               | 
               | Yeah but that ultimately requires an unsafe block, kind
               | of true, except no one reads the code of all crates they
               | depend on, and the direct dependencies might be safe in
               | what concerns the direct consumers.
        
               | bluejekyll wrote:
               | This is a point that you constantly bring up in these
               | threads, do you think most developers believe that data
               | race safety should extend beyond the bounds of the
               | process?
               | 
               | One thing that Rust's type system does allow you to do is
               | define a consistent manner in which to access external
               | systems, even add types that will mimic the same safety.
               | Is it perfect? Will it protect you from a different
               | process working against the DB? Will it enforce things in
               | the other process? No. But will it give you higher level
               | semantics to be able to construct a better model for
               | operating against that external system? Yes.
        
               | pjmlp wrote:
               | Yes, when they care about data consistency in distributed
               | systems.
               | 
               | Maybe many Rust devs don't care.
        
               | tialaramex wrote:
               | This has been a recurring theme from you, but in the
               | cases you're describing the risk is only a race condition
               | (no general solution is possible) and not a data race
               | (which safe Rust is able to deny by design). These are
               | categorically different problems.
        
               | pjmlp wrote:
               | Because it is a recurring theme to ignore the other kind
               | of race scenarios when promoting Rust's type system.
        
               | tialaramex wrote:
               | People have to consider race conditions _anyway_ ,
               | they're part of our world. For example if you use git's
               | ordinary --force to overwrite certain changes that's
               | subject to a TOCTOU race, which is why force-with-lease
               | exists. Even in the real world, I once opened a bedroom
               | window to throw a spider out onto the garden below and a
               | _different_ spider came in through the window in the
               | brief interval while it was open - exploiting the  "open
               | window to throw out spider" race opportunity.
               | 
               | Data race isn't just "Oh it's just a race condition" or
               | Rust wouldn't care, data races destroy Sequential
               | Consistency. And humans need sequential consistency to
               | reason about the behaviour of non-trivial programs. So,
               | without this writing concurrent software is a nightmare.
               | Hence, "fearless concurrency".
               | 
               | You won't destroy sequential consistency by having non-
               | transactional SQL queries. Try it.
        
               | lenkite wrote:
               | Is Rust really "fearless concurrency" ?
               | 
               | Considering the number of deadlock issues encountered by
               | folks using async Rust, I think "fearless concurrency" is
               | misinformation by Rust evangelists.
               | 
               | Deadlocks in production Rust microservice:
               | 
               | https://blog.polybdenum.com/2022/07/24/fixing-the-next-
               | thous...
        
               | pjmlp wrote:
               | I have tried plenty of times, and seen not so happy train
               | travelers with the same ticket for the same place on the
               | same train, hence why bring it up all the time.
               | 
               | It is obvious it is a subject that is irrelevant in the
               | Rust community.
               | 
               | Who needs consistency in distributed systems when
               | multiple threads from the same process are accessing the
               | same external data without coordination.
        
               | tialaramex wrote:
               | > Who needs consistency
               | 
               | Programmers do. Programmers are human and so can't reason
               | about the behaviour of non-trivial programs without
               | sequential consistency.
               | 
               | If I was trying to debug software which sometimes
               | mistakenly issues people duplicate tickets, I think I'd
               | want to be able to reason about how the software works,
               | and that's not going to be possible if it doesn't even
               | exhibit sequential consistency.
        
               | ncmncm wrote:
               | Your argument amounts to "Rust prevents X, so X is
               | important. Rust cannot prevent Y, therefore Y is less
               | important."
        
               | benreesman wrote:
               | Rust isn't a startup business or (one hopes) a religion,
               | or an MLM, or a home for sale. It's a useful tool among
               | many.
               | 
               | Why do people say things like: "It's better not to dilute
               | the message"? Better for who? That's sales/marketing
               | language, not engineering language.
               | 
               | "The message"? Pardon my Francais, but WTF?
        
               | dannymi wrote:
               | >Why do people say things like: "It's better not to
               | dilute the message"?
               | 
               | >Better for who?
               | 
               | Better for everyone.
               | 
               | When talking about a new thing, it would be really silly
               | to emphasize how nice the logo is, how nice the package
               | it comes in is, look at the awesome tape the box is
               | closed with etc. If I turn the product off it even turns
               | off! Look at the nice rounded corners of the device!
               | 
               | It even can do async! Just like Javascript and .NET.
               | 
               | Who cares!
               | 
               | What is the main strength of the tool, the pain point it
               | was made to eliminate? Lead with that.
               | 
               | > That's sales/marketing language, not engineering
               | language.
               | 
               | Leading with the actual technical novelty that actually
               | advances the state of the art in production compilers is
               | marketing? Well, I guess it's good marketing in a way.
               | 
               | The user will find cargo on their own in 5 minutes.
        
               | ChadNauseam wrote:
               | Even if rust wasn't memory safe, being C++ with traits
               | and ADTs would be enough for me to use it. Those are
               | important safety features!
        
               | benreesman wrote:
               | I mean "message" how you want, HN is hosted in a free
               | country.
               | 
               | But N=1 for you: as a serious polyglot user of Rust who
               | knows it well and uses it all the time: this shit is a
               | _huge_ turnoff. It 's a programming language. On a long
               | enough timeline all the motivated hackers will end up
               | knowing many programming languages well, they all have
               | pros and cons.
               | 
               | Trying to boil important engineering decisions down to a
               | tweet so that we can stay "on message" comes off like
               | something someone would do if they were selling books or
               | training or consulting services attached to a technology,
               | which a priori gives them an agenda other than giving
               | good advice.
               | 
               | So to keep it short: help people pick the right tool for
               | the job without an agenda.
        
               | bluejekyll wrote:
               | Rust is a programming language. It's the right tool for
               | the job of programming. It happens to have a lot of
               | features that make it a very good programming language.
               | 
               | I think you're wrong about the language and community,
               | though. It's killer feature is it's safety, be it memory,
               | data race, or type. These are the reasons I was
               | interested in learning the language. The fact that the
               | tools make that easier is why I was able to struggle
               | through the new concepts and actually be able to build
               | useful things with it.
               | 
               | If the fact that people enjoy something as a general
               | community turns you off, that's not the community's
               | problem.
        
               | jamincan wrote:
               | I think you have a point. Rust is primarily focused on
               | being a systems language, and memory safety is the killer
               | feature it brings to the table in that domain. But we
               | know that Rust is being used in areas where its qualities
               | as a systems language are less important.
               | 
               | Why, for example, would a Python developer pick up Rust?
               | Probably because of the really strict typing addressing a
               | major pain point for most Python developers and the trait
               | system being somewhat analogous to Protocols, which any
               | Python developer who has chafed with the dynamic typing
               | is almost certainly already familiar with. With good
               | library support for interfacing between the two, it's a
               | more natural coupling than most people would think on the
               | face of it.
               | 
               | That said, while I don't think a Python developer reaches
               | for Rust because of memory safety, I do think it's still
               | an important factor as it provides the guard rails that
               | make it so someone who has primarily used a GC language
               | and not had to concern themselves as much with managing
               | memory can start using Rust knowing that the compiler is
               | not going to let them accidentally shoot them in the foot
               | when it comes to memory management.
        
               | nyanpasu64 wrote:
               | rustc rejects the resulting program if you mechanically
               | factor out a function accessing &mut self, into a
               | function holding mutable borrows to half the fields
               | calling another function which access the other half of
               | fields (or vice versa, the caller holding &field calling
               | a method mutating other fields). This requires the more
               | complex transformation of passing individual fields into
               | the subfunction (more work, but sometimes easier to
               | read), or waiting for Rust to add partial borrows. Note
               | I've never actually hit this case myself, though I've
               | heard it's an issue people run into.
        
               | BoardsOfCanada wrote:
               | I think memory safety is the killer feature of rust, and
               | has become so because people see the real world problem
               | it's solving, more than through evangelicalism. We'll see
               | in a few years when more "heavy shit" has been
               | written/rewritten in rust. My prediction is that they
               | will have significantly fewer memory safety issues than
               | comparable c++ "heavy shit".
        
               | pjmlp wrote:
               | Business also needs to care,
               | 
               | > Many years later we asked our customers whether they
               | wished us to provide an option to switch off these checks
               | in the interests of efficiency on production runs.
               | Unanimously, they urged us not to--they already knew how
               | frequently subscript errors occur on production runs
               | where failure to detect them could be disastrous. I note
               | with fear and horror that even in 1980, language
               | designers and users have not learned this lesson. In any
               | respectable branch of engineering, failure to observe
               | such elementary precautions would have long been against
               | the law.
               | 
               | -- C.A.R Hoare on his Turing award speech in 1981.
        
               | benreesman wrote:
               | From where I sit the killer feature of Rust is that a
               | bunch of amazingly cool software is written in it,
               | especially in the terminal. I'm a big terminal guy, and I
               | can't think off the top of my head of anything I use
               | constantly that _isn 't_ written in Rust. `rg`, `fzf`,
               | `zoxide`, `bat`, `viddy`, the list goes on and on, I
               | fucking love the shit people are writing in Rust.
               | 
               | And I think that should be the killer feature of a
               | language: that cool software is written in it and is
               | continuing to be written in it. This is a killer feature
               | shared by Rust and C++ and these days to be serious about
               | performant software in diverse settings, you pretty much
               | have to know both well.
        
               | jamincan wrote:
               | Terminal programs are one area where Rust's strengths
               | seem to align (very good CLI libraries/parsing, error
               | management, and concurrency) and weaknesses are less
               | relevant (async, GUIs), which might be why it seems to be
               | gaining traction in that area.
        
               | Narew wrote:
               | btw, fzf is written in go ^^
        
               | matheusmoreira wrote:
               | > We'll see in a few years when more "heavy shit" has
               | been written/rewritten in rust.
               | 
               | I'd really like to see that. Would be cool to see a
               | completely new Linux user space written in Rust. Not
               | necessarily a rewrite of existing software, new ideas
               | would be great. I tested Linux system calls and they
               | worked very well even though they needed experimental
               | inline assembly functionality to work. With system call
               | support, anything is possible.
        
               | pjmlp wrote:
               | Once upon a time there was a project to do a Linux
               | distribution in Ada.
               | 
               | Unfortunately it died a couple of years later.
        
               | nibbleshifter wrote:
               | Slowly there's more and more implementations emerging of
               | Linux userland utils in Rust.
               | 
               | It's taking a while.
        
               | Rusky wrote:
               | > a) it's not actually memory safe once you start doing
               | heavy shit b) modern C++ is quite memory safe
               | 
               | This just doesn't capture the problem that memory safety
               | solves. A crashed program is not the worst-case scenario
               | that it's trying to avoid. Even the most memory-safe
               | language supports exiting early with an error message, or
               | whatever.
               | 
               | In terms of language semantics, there is an all-or-
               | nothing line between memory safety and undefined
               | behavior. A memory safe program does what it says,
               | locally, step-by-step, according to the semantics of the
               | language. When a program exhibits UB, those guarantees
               | are lost.
               | 
               | Of course, as you note, unsafe Rust also lets you violate
               | memory safety, and in fact any memory safe language is at
               | the mercy of its implementation and host. The reason
               | people get evangelical about Rust's memory safety is one
               | level higher: it offers a bridge _back_ to memory safety,
               | such that unsafe code stands on the same footing as the
               | core language. When either are bug-free, the compiler can
               | ensure they are used correctly, using the same type
               | system features for both.
               | 
               | Modern C++ is certainly much less error-prone than the
               | bad old days of manual `new` and `delete`, but it doesn't
               | have an answer to this "unsafe encapsulation." To the
               | contrary, modern C++ actually adds a bunch of _new_ ways
               | to violate memory safety by misusing library APIs.
               | Iterator invalidation, use-after-move, string_view and
               | span and borrowed ranges, by-reference lambda and
               | coroutine captures, etc.
               | 
               | This all means that "serious systems software" in C++ has
               | to approach memory safety via defensive copying or
               | refcounting, copious use of sanitizers, and sandboxed
               | sub-processes. Meanwhile, Rust programs can do things
               | that would be unthinkable in a large C++ codebase,
               | because the assumptions of both the language and unsafe
               | code are encoded in the type system. (For example:
               | https://manishearth.github.io/blog/2015/05/03/where-rust-
               | rea...) It's a qualitatively different solution to the
               | problem.
        
               | jjnoakes wrote:
               | > a crashed program is a crashed program whether I
               | dereferenced a null pointer or was poking around in a
               | slice with multi-byte Unicode characters in it
               | 
               | They aren't the same thing though, that's the point.
               | 
               | Dereferencing a NULL pointer isn't guaranteed to crash.
               | In fact if you are writing through the pointer, you may
               | even have a security issue on your hands (rce, etc).
               | 
               | Safe Rust may have runtime errors that "crash" the
               | program but this is a controlled, well-defined
               | termination, and there is no way for the execution state
               | itself to be corrupted like in C++.
        
               | vvanders wrote:
               | Yep, my favorite part of heap/stack corruption is not
               | when it crashes immediately but rather when it rears its
               | head 2-3 weeks/months later when some upstream call
               | pattern or timing has changed.
               | 
               | I've spend _weeks_ chasing down single instances of this
               | on multiple projects. The nasty part is you have no
               | predictability in if it 's going to be one that crashes
               | immediately, silently writes garbage(hopefully not to
               | disk!), is a latent lurking crash or security vuln.
               | 
               | If you trash the stack then there's a good chance you
               | lose the backtrace as well which can make a hard to debug
               | issue become "find the needle in the haystack". I hope
               | it's something that reproduces quickly and consistently
               | because otherwise you're in for a ride.
        
               | ncmncm wrote:
               | It has been years since I spent any time chasing down
               | memory usage errors.
               | 
               | I recommend compiling with warnings turned on, and acting
               | on them.
        
               | ncmncm wrote:
               | Rust is "cool", lately. But it _lacks basic features_
               | that enable capturing important semantics in libraries.
               | 
               | So the tradeoff is not relative safety against a little
               | compile-time inconvenience. The tradeoff is against "no,
               | you cannot code that thing at all, suck it".
               | 
               | So almost all discussion of relative safety (which Rust
               | advocates would like us to think is absolute) carefully
               | sidesteps the point that there is a very great deal that
               | cannot be expressed in Rust at all -- and not because
               | expressing those things would have been at all unsafe.
        
       | jiehong wrote:
       | Java falls a bit in between Go and Rust on that example:
       | 
       | Closures only allow variables that are effectively final, aka you
       | can't reassign them (the compiler will stop you).
       | 
       | But you could pass an object and update its internal state.
        
         | qsort wrote:
         | "final" in Java is kind of useless, really. A const mechanism
         | like C++ has would go a long way and would be a perfect fit for
         | the OO nature of the language.
        
           | eganjs wrote:
           | Final in Java means the value of a variable, property or
           | parameter will not change after its initial assignment.
           | Values in Java can be either a reference to an object or a
           | primitive such as an integer, double or bool. It's definitely
           | far from useless as it asserts that the reference or value
           | you capture in a closure is not an old version that has been
           | replaced, this approach is a consequence of Java disallowing
           | arbitrary pointers. IMO Java's biggest mistake here is
           | mutability by default, which Kotlin has learned from. If you
           | understand why it is this way it makes a lot of sense and tbh
           | I think it promotes better code. That said I would like to
           | see more immutability in Java and with things like Record
           | classes you can see Java is moving in the right direction.
        
             | qsort wrote:
             | I agree with that and I use final as much as possible. E.g.
             | instance variables that don't need to change, I almost
             | religiously declare them as "final" and initialize them in
             | the constructor.
             | 
             | What I mean is that what Java really "should" have is const
             | like C++. A C++ function with a prototype of:
             | int doSomething(std::vector<int> const &x)
             | 
             | tells me much more than the equivalent Java:
             | int doSomething(final List<Integer> x)
             | 
             | Also C++ member functions can declare themselves as not
             | modifying their "this" instance. E.g. there's no way to
             | write this code in Java:                  int
             | X::doSomething(std::vector<int> const &x) const {
             | ...        }
             | 
             | which is an extremely powerful, compile-time checkable
             | description of what we are doing.
             | 
             | It's not that final is useless (probably wrong choice of
             | words there), what it does is okay and it's correct to use
             | it as much as possible, but it's a far cry from the static
             | guarantees afforded by const-correct code.
             | 
             | I also agree that the correct approach is immutability by
             | default, but that ship has sailed, and it's also an
             | orthogonal concern to what I'm saying here.
        
       | kcartlidge wrote:
       | Totally not the point of the article, and totally _subjective_ I
       | know, but to me the thing that jumps out is how much more
       | readable the Go code is than the others.
        
         | deeptote wrote:
         | This is the main reason that I use Go as my main language, and
         | why many orgs are starting to adopt it: it's easy to read and
         | jump into. I would argue, however, that it's very easy to
         | create antipatterns and just general spaghetti code with Go. A
         | language that's easy to be productive with != one that's also
         | easy to maintain. Design and philosophy becomes very important
         | with large codebases in the language.
         | 
         | source: consultant, seen some truly heinous Go monoliths.
        
           | bsaul wrote:
           | There are definitely some ways to do bad designs in go, but i
           | have the feeling it will be more immediately apparent what's
           | wrong (or at least what part of the system needs rework). The
           | reason being that there are no ways to obfsucate an awful
           | design by wrapping it on mountains of generics programming
           | and language sugar, making the whole thing a lot worse.
           | 
           | It's only my gut feeling, but does that match your experience
           | ?
        
         | arriu wrote:
         | I agree. However, my gut reaction was that the style of the go
         | code was written differently than what I'd expect if asked to
         | work off the rust version.
        
         | agent281 wrote:
         | Definitely subjective. I don't find some parts very readable.
         | E.g., this line took my a minute to parse:
         | append34 := func() func([]int) []int {
         | 
         | If I was going to rank the readability I would say:
         | 
         | 1. Rust function bodies 2. Go code 3. Rust function signatures
         | 4. C++ code
         | 
         | Which you could argue is me shifting the boundaries a bit, but
         | sufficiently statically typed languages seem to develop two (or
         | more) sublanguages. Global complexity definitely pushes Rust
         | down peg.
        
           | andai wrote:
           | I don't write Go but it might have something to do with this:
           | 
           | https://go.dev/blog/declaration-syntax
           | 
           | Go's type syntax is unusual but supposedly much clearer when
           | things get more involved.
        
         | [deleted]
        
         | hkalbasi wrote:
         | Can you mention the difference that made Go code is more
         | readable than C++ and Rust?
        
       | wruza wrote:
       | _comparaison_
       | 
       | Probableur a typeaux?
        
         | [deleted]
        
       | rob74 wrote:
       | Was this posted by a French speaker? Because in English it's
       | "comparison", not "comparaison"...
        
       | jeffbee wrote:
       | There's a solid argument in here but it feels like there must be
       | a better example. Can we think of a function that does something
       | worth doing, in a way that programmers of all these languages
       | would actually use, and that sets a subtle trap for C++
       | programmers? When I read this article all I see is a useless
       | function that contains a completely obvious trap which, yes, Rust
       | prevents, but also just thinking at all would have prevented.
       | 
       | Another small thing: the C++ in this article looks weird to C++
       | programmers because it qualified vector with std, but does not
       | qualify move.
        
       ___________________________________________________________________
       (page generated 2022-07-30 23:01 UTC)