[HN Gopher] C++ Papercuts
       ___________________________________________________________________
        
       C++ Papercuts
        
       Author : signa11
       Score  : 136 points
       Date   : 2023-08-28 11:06 UTC (11 hours ago)
        
 (HTM) web link (www.thecodedmessage.com)
 (TXT) w3m dump (www.thecodedmessage.com)
        
       | zoogeny wrote:
       | > There are so many features I miss from Rust ... like sum types
       | (called enums in Rust)
       | 
       | I feel the same whenever I have to go to other languages after
       | using TypeScript. I want to model all of my data using the
       | limited algebraic data type operations that many newer languages
       | have (usually sum and product types).
       | 
       | Recently I've been writing some Go for a project that is a tiny
       | bit beyond a plaything. I am surprised how much I actually like
       | Go and I am very glad to have come to the language to use for
       | real after they added generics. But several times I wanted to do
       | a union `SomeType | AnotherType` and the fact it is missing from
       | Go is like an itch that can't be scratched.
       | 
       | I would also add that discriminated unions alongside type
       | narrowing (usually based on branching or escape analysis) just
       | seems like something I want everywhere. Add in a powerful `match`
       | statement and some kind of destructuring and I start to get very
       | interested.
       | 
       | If there is some future where Go picks up sum types, adds some
       | kind of support for discriminated unions and type narrowing based
       | on branching - then it might just be the best general purpose
       | language (although unlikely to be a contender for best low-level
       | systems level language).
        
       | Night_Thastus wrote:
       | >const is not the default
       | 
       | While I agree to a point, I also understand the argument against
       | this. In programs, a lot of state needs to be changing. Which
       | means you're going to type mut a LOT over the course of a 100k
       | line program. And frankly, that gets tedious really quickly.
       | 
       | If you miss a const in C++, no big deal. The program still works.
       | It's mostly a safety net. [with notable exceptions] But if you
       | forget a mut in Rust, the program is not going to work.
       | 
       | That's an advantage (you can't write something incorrectly) but
       | it's also tedious, imo.
       | 
       | When working on the C++ codebase daily, I occasionally find
       | myself going "Hmm, that could be const/I need const here to use
       | something" and swap it over. But it's fairly rare, and overall
       | less time investment on my end.
       | 
       | Frankly, this explains most differences in Rust and C++. Rust is
       | OK with the developer having to do more boilerplate, more verbose
       | code, etc. for the sake of ensuring correctness. C++ assumes the
       | best and goes with it to save some time in the short run. That's
       | neither good nor bad, just preference.
        
         | bombela wrote:
         | > That's neither good nor bad, just preference.
         | 
         | I would argue that one of them giving you a better chance at
         | producing reliable software should go above and beyond the
         | notion of preference.
        
           | Night_Thastus wrote:
           | Something const would catch is rarely the cause of broken
           | software. In my experience, most issues in software are
           | logical and often derived from complex behaviors and
           | interactions, not misuse of syntax.
        
         | Measter wrote:
         | > While I agree to a point, I also understand the argument
         | against this. In programs, a lot of state needs to be changing.
         | Which means you're going to type mut a LOT over the course of a
         | 100k line program. And frankly, that gets tedious really
         | quickly.
         | 
         | It may not be as large a proportion as you think. Doing a quick
         | grep on the rustc compiler source (483k SLoC), there are 5431
         | matches for "let mut [\w\d_]+ =", and 29520 for "let [\w\d_]+
         | =". This is, of course, is only simple bindings and misses out
         | on more complex patterns along with if-let and match
         | expressions, but even so, about 84% aren't mut bindings.
         | 
         | One of my own codebases (including all its Rust dependencies is
         | about 1.5m SLoC) comes to about 80% not being mut. Ripgrep +
         | dependencies (718k SLoC) is about 77% not mut.
        
         | Buttons840 wrote:
         | > If you miss a const in C++, no big deal. The program still
         | works. It's mostly a safety net. [with notable exceptions] But
         | if you forget a mut in Rust, the program is not going to work.
         | 
         | I just can't take your argument seriously. One of your
         | arguments is "the program will still work, with notable
         | exceptions". I'm not persuaded.
         | 
         | You say it's a matter of preference, fair enough. I say let's
         | make companies financially liable for the consequences of their
         | preferences and see how this shakes out.
        
           | Night_Thastus wrote:
           | Every language, every rule, every suggestion has exceptions
           | and gotchas. That's just life, and programming in general.
           | 
           | I can't think of anything significant off-hand, it was just
           | more to stave off this part from the post:
           | 
           | >If you take a parameter by non-const reference, the caller
           | can only use lvalues to call your function. But if you take a
           | parameter by const reference, the caller can use lvalues or
           | rvalues. So some functions, in order to be used in natural
           | ways, must take their parameters by const reference.
           | 
           | >I say let's make companies financially liable for the
           | consequences of their preferences and see how this shakes
           | out.
           | 
           | This is completely unrelated. It's also a bit of a dangerous
           | road to go down and I'd suggest thinking about it a lot more.
        
         | maratc wrote:
         | const never really worked with other C++ features[0], so in any
         | big pile of code you're better off not using const.
         | 
         | [0] The compiler will gladly pass a non-const pointer to a non-
         | const object to a function that expects a const pointer to a
         | const object; it all "makes sense". However, a compiler will
         | explode when you're trying to pass a non-const vector of non-
         | const objects to a function that expects a const vector of
         | const objects.
        
           | spacechild1 wrote:
           | > a function that expects a const vector of const objects.
           | 
           | The elements of std::vector cannot be const.
        
       | TinkersW wrote:
       | The post seems like it is focused on some ancient version of C++,
       | multiple of these papercuts aren't even valid today.
       | 
       | * You can just write = default for those methods often enough,
       | and having to implement these is extremely rare
       | 
       | * use [[nodiscard]] for your silly bool get_message return
       | 
       | * C++ does in fact support multiple return types via structured
       | bindings, or just use a struct
       | 
       | None of this is really what is wrong with C++, the real issues
       | are that everyone uses a different build system and a different
       | way of sharing code.
        
         | thecodedmessage wrote:
         | No, you cannot use '= default' for the assignment operators if
         | you have custom copy/move constructors. I thought this was
         | clear that this was my complaint in the post. I will make it
         | clearer in the post. IF you customize the constructors, THEN
         | =default will break your code, even though the correct
         | implementation is entirely boilerplate.
         | 
         | And for tuples, you still need to use std::tie or
         | std::make_tuple on the other end.
        
       | gituliar wrote:
       | > Unfortunately, I am all too well aware of why these decisions
       | were made, and it is exactly one reason: Compatibility with
       | legacy code. C++ has no editions system, no way to deprecate core
       | language features.
       | 
       | Actually, I'm curious if somebody is working on a flavor of the
       | modern C++ that abandons backward compatibility ?
       | 
       | The Carbon project feels like a completely new language. What I'd
       | expect instead is something that adopts best features and
       | practices from modern C++, so that an average C++ project would
       | require just minor changes to migrate, in spirit of Python 2 -> 3
       | migration.
        
         | omoikane wrote:
         | A new language will eventually acquire its own grungy legacy
         | just like C++ did, unless that new language aggressively breaks
         | backward compatibility.
         | 
         | I think the best practice is for people to stick to a subset of
         | safe patterns in mature languages. For C++, that's probably the
         | C++ Core Guidelines:
         | 
         | https://isocpp.github.io/CppCoreGuidelines/
        
         | doodpants wrote:
         | Have you looked at the CppFront[1] project by Herb Sutter? It's
         | less of a completely new language like Carbon. It's meant to be
         | C++ with a "nicer" syntax, while providing 100% linking
         | compatibility, and with 100% source backward compatibility
         | available when desired.
         | 
         | [1] https://github.com/hsutter/cppfront
        
         | [deleted]
        
         | pjc50 wrote:
         | > I'm curious if somebody is working on a flavor of the modern
         | C++ that abandons backward compatibility ?
         | 
         | This is just Rust. You can't really deprecate bare pointers in
         | C++ without completely wrecking the language.
        
           | HALtheWise wrote:
           | This... seems false? In particular, the point of an "edition"
           | system is that code written for the new edition can
           | seamlessly import code written for the old edition, and so a
           | hypothetical pointerless "new edition" would simply disallow
           | bare pointers in new files, and things like smart pointers
           | would continue to be _implemented_ in old edition code.
        
             | pjc50 wrote:
             | > would simply disallow bare pointers in new files
             | 
             | And all their header files. That's a real Year Zero moment:
             | you've just cut yourself off from the standard library, as
             | well as most of the code you might want to link to. I don't
             | think there's any "simpler" about it.
        
             | steveklabnik wrote:
             | Bringing editions to C++ failed, and I am not aware of
             | anyone trying to tackle the issues
             | https://github.com/cplusplus/papers/issues/631
             | 
             | (I could be wrong though! I follow the committee more than
             | you may guess, but not as much as to think I know
             | everything about what's going on.)
        
         | [deleted]
        
         | TillE wrote:
         | You can get to a pretty clean modern C++ by convention and
         | compiler warnings and static analysis.
         | 
         | Carbon _is_ a completely new language, and that 's the point.
         | Part of the language design is making compilation a lot faster,
         | for example, as well as features that C++ can't realistically
         | implement, while retaining C++ interop. It's an exciting way
         | forward if they manage to achieve their goals, and it _would_
         | be a way to gradually migrate C++ projects.
        
       | smallstepforman wrote:
       | Well, at least with C++ you can write linked lists and other
       | algoritms which require multiple references. Not every engineer
       | is sloppy with use-after-free, incorrect mutability, leaking
       | resources etc. There are correct programs written in C++, the
       | developers are not unicorns and it does not require super human
       | effort. Kitchen knives are also unsafe and dangerous yet with
       | their correct use we can create wonderful meals.
        
         | clnq wrote:
         | > Kitchen knives are also unsafe and dangerous yet with their
         | correct use we can create wonderful meals.
         | 
         | This is very well put and mirrors my sentiment as a long-term
         | C++ swe.
         | 
         | I also think that the following quote is rather
         | misrepresentative:
         | 
         | > C++ is a great programming language. The complaints are just
         | from people who aren't up to it. If they were better
         | programmers, they'd appreciate the C++ way of doing things, and
         | they wouldn't need their hand-held. Languages like Rust are not
         | helpful for such true professionals.
         | 
         | C++ _is_ a great language and a lot of complaints _do_ come
         | from people who aren 't up to learn it properly. This isn't
         | elitism, and this state of things has been true for every
         | skill-based discipline in history. I am not saying that I am
         | better than people who refuse to learn C++, or that C++ is a
         | better language than others - I don't think there are many C++
         | programmers at all who do, it would be a very ridiculous
         | stance.
         | 
         | C++ is a great language, it is also a somewhat dangerous
         | language. It is not a language for everyone and every purpose.
         | And that is fine. Just like knives are great instruments even
         | if they are a bit dangerous and unsafe to those who do not wish
         | to learn it properly and where it makes sense.
         | 
         | At the end of the day, it doesn't matter how many people who
         | just don't see a problem with C++ one calls "elitist" or other
         | things, not everyone is obsessed with micro-improving a
         | language that clearly works very well.
        
         | pjc50 wrote:
         | > There are correct programs written in C++
         | 
         | How do you know _correct_? Very few pieces of software are
         | subject to full theorem-prover correctness, and very few pieces
         | of C++ are completely free from ever invoking  "undefined
         | behavior" at runtime. Because that's extremely hard to do when
         | even "x = x + 1" can be UB.
        
           | AnimalMuppet wrote:
           | By that standard, how do you know _correct_ programs are
           | written in any language? I seem to recall you praising JOVIAL
           | a week or two ago; well, how many full theorem-prover correct
           | programs were written in JOVIAL? I would guess, zero.
           | 
           | Don't hold C++ programs up to a standard that programs in
           | other languages also can't pass.
        
             | pjc50 wrote:
             | > I seem to recall you praising JOVIAL a week or two ago
             | 
             | Think you have the wrong person? I have no idea what this
             | refers to.
        
               | AnimalMuppet wrote:
               | Well, I'll take your word on it. I was working from
               | memory, not looking at a particular post. I apologize for
               | the inaccurate claim.
               | 
               | Back to the larger point: How many Rust programs meet
               | your proposed level of "correctness"? How many Fortran
               | programs, Java programs, Python programs? Virtually none
               | of our software does. Why hold C++ to a standard that
               | nothing else meets either?
        
         | rhn_mk1 wrote:
         | IMO not every coder is sloppy with use-after-free. Not everyone
         | is slopy with incorrect mutability. [...] Not everyone is
         | sloppy with $THING[N]. But it's hard to consistently not be
         | sloppy in any one of them, and N are so many that almost
         | everyone is sloppy with at least one of them, most of the time.
         | 
         | Each probablility of a mistake may be low on its own, but they
         | compound with every avenue to make mistakes.
        
           | evouga wrote:
           | I've spent hours helping my students debug their Rust
           | programs of incorrect array-indexing logic, incorrectly-
           | reasoned loop invariants, incorrect conditional logic, and
           | everything in between. They're not double-deleting raw
           | pointers, but then again, most C++ programs don't use raw
           | pointers these days either...
           | 
           | "Rust makes a few classes of bugs impossible to write by
           | construction" is a very nice feature of the language. But I'm
           | really turned off by how insufferable the Rust community is
           | (which shines through in the OP's blog post and several
           | comments elsewhere in the responses): it's never the quote at
           | the start of this paragraph; it's always "writing software in
           | anything but Rust is fundamentally irresponsible!" or "if a
           | Rust program compiles, it must be correct!" No, not even
           | close.
        
       | lelanthran wrote:
       | Funnily enough, when I joined a C++ team about 4 years ago I
       | could never get _any_ senior dev to move away from using
       | references instead of const pointers.
       | 
       | Reference parameters are awful; the reader has no idea whether
       | the argument will be modified or not _without reading the header
       | file_.
       | 
       | With const pointers you never have this problem.
        
         | dureuill wrote:
         | with const pointers you have the different problem that you
         | need to define what happens when the nullptr is passed.
         | 
         | While I would certainly prefer the reference to be visible at
         | the call site (a mistake Rust did not repeat), if I have to
         | choose, I prefer the inconvenience of having to use an IDE like
         | qtcreator that will display references in a different style, to
         | introducing an invalid value that needs to be handled (and will
         | be mishandled).
         | 
         | So yeah, as a senior dev, it checks out.
        
         | YesBox wrote:
         | I use this solution as a solo dev:                 #define $
         | 
         | And add the $ in front of all non-const pass by reference
         | parameters in the call
        
         | MereInterest wrote:
         | With const pointers, you have no idea if the parameter is
         | implicitly optional, if it must point to a valid object, if it
         | will throw an error when provided with a null pointer, if it
         | will segfault when provided with a null pointer. Not even
         | reading the header file will help you here.
         | 
         | With const references you never have this problem.
        
           | alexvitkov wrote:
           | Yes, but non-const references are utter trash since you can't
           | tell you're passing a mutable reference when looking at the
           | call site, so for the non-const I'll always pass by pointer.
           | 
           | Const references are less offensive, but I'm not going to
           | pass non-const by pointer and const by reference, that's just
           | inconsistent for no reason.
           | 
           | If null is not a valid value, throw an assert at the
           | beginning of the function. It's not like references can't be
           | in a broken state, they're just syntax sugar for pointers
           | (that also happens to wildly complicate the language's type
           | system)
        
             | ninkendo wrote:
             | > Const references are less offensive, but I'm not going to
             | pass non-const by pointer and const by reference, that's
             | just inconsistent for no reason.
             | 
             | Why use pointers at all? Just use const references for
             | immutable stuff, regular references when you want mutation,
             | normal values when you want copy semantics, and smart
             | pointers for other cases (unique_ptr for move semantics,
             | shared_ptr for lack of clear ownership, etc.) If you want
             | to represent nullability, std::optional is a good choice.
             | 
             | > If null is not a valid value, throw an assert at the
             | beginning of the function
             | 
             | Yikes. This means the compiler will not catch it for you.
             | Why make this a runtime issue? Use the type system to your
             | advantage.
        
               | alexvitkov wrote:
               | If references were guarenteed sane and consistent values
               | like in Rust, sure - but it's trivial to get a bogus
               | reference in C++, so I don't buy the type safety thing.
               | An assert actually catches errors, a reference just
               | declares intent.
               | 
               | As for smart pointers there's a lot of domains with tight
               | performance requirements where you can't afford them -
               | and to be quite frank, if you can afford to use
               | shared_ptr you can also afford GC, so just use a sane
               | language
        
             | MereInterest wrote:
             | Yeah, I'd beg to differ there. Non-const references have
             | all the non-nullable benefits over pointer-to-non-const as
             | const references have over pointer-to-const. While I'd
             | prefer a call-site syntax difference similar to what Rust
             | has, the non-null ability of references is well worth it.
        
         | ninepoints wrote:
         | IMO read the damn header file and abide by the api contract. A
         | const pointer type suggests nullability, and possibly even
         | pointer arithmetic is permissible.
        
         | TonyTrapp wrote:
         | How does a const pointer express this in a way a const
         | reference can't?
        
           | dtho wrote:
           | In the absence of IDE features, you can't differentiate
           | between value arguments and reference arguments unless you
           | view the callee's prototype. Pointers do not have this
           | problem.
        
             | TonyTrapp wrote:
             | In the absence of IDE features, you also can't
             | differentiate between a function taking a pointer and a
             | const pointer. You must be aware in _all_ cases that the
             | value that you pass to the function may be modified. Yes,
             | the function would not be modifying the pointer itself, but
             | the outcome is exactly the same. A reference is pretty much
             | equal to a non-nullable pointer in this context.
        
               | lelanthran wrote:
               | > In the absence of IDE features, you also can't
               | differentiate between a function taking a pointer and a
               | const pointer.
               | 
               | So? It still helps me with decoding `foo(bar, baz)`, as
               | without the `&` I _KNOW_ that bar and baz would not
               | change after the call. And since both bar and baz are in
               | the local scope, I already _know_ whether they are
               | pointers or not.
               | 
               | With pointers, all the information necessary to determine
               | if bar and baz would change after a call to foo is in the
               | local scope. With references that information is
               | elsewhere.
        
         | gumby wrote:
         | A pointer can be nullptr while a reference cannot so that would
         | be a step backwards.
         | 
         | Also the "problem" you describe is not solved by your proposed
         | "solution". A const pointer can point to something that can be
         | modified.
         | 
         | You can declare a pointer to a const object, or, you know,
         | declare a const ref.
        
           | lelanthran wrote:
           | [EDIT: Brainfarted in the last paragraph, read "const ref" as
           | "ref"]
           | 
           | > A pointer can be nullptr while a reference cannot so that
           | would be a step backwards.
           | 
           | Backwards from what? A reference can't be null, but it can
           | still be invalid, which is a more common problem than
           | forgetting to check for null-ness.
           | 
           | > You can declare a pointer to a const object,
           | 
           | Yes. I should have said that instead.
           | 
           | > , or, you know, declare a const ref.
           | 
           | That doesn't solve the original problem with references,
           | though. I hate reading `foo (bar, baz)` and having to dig
           | into the header to find out that bar or baz or both might be
           | modified after the call to foo.
           | 
           | When reading code, I want to be able to skip over anything
           | that does not modify the state of variables in the current
           | scope.
           | 
           | Using references means that I have to know what every single
           | call does. Using pointers exclusively, I can simply skip
           | calls to functions with arguments that aren't pointers.
        
             | dureuill wrote:
             | > Backwards from what? A reference can't be null, but it
             | can still be invalid, which is a more common problem than
             | forgetting to check for null-ness.
             | 
             | no, a reference cannot be invalid. If you are given an
             | invalid reference (such as referring to an object after its
             | lifetime ended, or referring to something that is not an
             | object of the reference's type), then the fault lies on the
             | caller. The callee cannot check the reference's validity,
             | nor it is its responsibility to do so.
             | 
             | This is *very* different from the case of a pointer, where
             | the caller is not at fault, typesystem wise, for giving a
             | nullptr to a function accepting a pointer.
             | 
             | Increasing the probability of runtime errors to gain a
             | little bit of readability at the caller's site is not a
             | good tradeoff.
        
           | shortrounddev2 wrote:
           | Microsoft created SAL so you can annotate pointers to
           | instruct the compiler to put up warnings or even errors for
           | null required fields:                   _Success_(return)
           | //out parameters should be set if this returns true
           | bool tryGetValue(           _In_ Map const* map, //pointer is
           | for input only and cannot be null           _In_ char const*
           | name,           _Out_opt_ Data* value //pointer is for output
           | and can be null         );
           | 
           | If you pass null for _In_ params, the compiler will warn or
           | error. You return true without setting the _out_opt_
           | parameters, the compiler will warn or error. You can pass
           | null to _out_opt_, and the compiler will warn or error if the
           | function body doesn't properly check if the parameter is null
        
             | fluoridation wrote:
             | Of course, that only works if you're passing a literal
             | null. If you're passing a variable the compiler will not
             | check anything, so this doesn't really solve the problem.
        
               | jprete wrote:
               | The annotation does need to be propagated backwards to
               | callers and declarers - and there needs to be a way to
               | "annotate" an existing pointer in the scopes where you've
               | proven that it's not null - but that's better than having
               | no annotation at all.
               | 
               | (Although this is something I'd 100% want C++ to do, and
               | not with an annotation but with a very short operator or
               | reserved word.)
        
         | gpderetta wrote:
         | If you pass a pointer (or anything with reference semantics) by
         | value you can still get non local mutation through it without
         | obvious call site syntax.
         | 
         | There is no perfect solution. Avoid out parameters if possible,
         | otherwise make the semantics clear via function naming or at
         | least parameter naming conventions.
        
         | fsloth wrote:
         | "get any senior dev to move away from using references instead
         | of const pointers." ... "the reader has no idea whether the
         | argument will be modified or not without reading the header
         | file."
         | 
         | It sounds like the problem is not using an IDE.
         | 
         | Use references whenever posssible. Then you don't need to do a
         | null pointer check. C++ is a footgun. And like with all guns,
         | security first. With guns - put the safety on. With C++ - check
         | the pointers are not null.
         | 
         | OFC one can argue that with proper instrumentation invalid
         | pointers are captured sooner than later, but the fact is it's
         | much easier to represent invalid program state using pointer
         | than references.
        
           | thecodedmessage wrote:
           | Yes, the "C++ is not usable on its own and needs other tools"
           | defence of C++ :-)
        
           | PaulDavisThe1st wrote:
           | In my experience (31 years of C++) it is much more likely
           | that problematic pointers will be non-null but also invalid.
        
         | ant6n wrote:
         | Cant u use a const reference?
        
         | nly wrote:
         | Use a decent IDE. Most render & at the call site so you know.
         | 
         | I find this all a bit rich anyway. In languages like C#,
         | _everything_ is a bloody reference and this lame concept of
         | "reference types" corrupts developer minds.
        
           | tubs wrote:
           | Java and c# are both pass by value (c# has optional ref that
           | must be annotated at both the call site and definition, it
           | also has value structures).
           | 
           | Class types are effectively pointers that are passed by the
           | value of the pointer.
           | 
           | You cannot implement swap in Java without eg wrapping the
           | params in one element arrays.
        
           | raincole wrote:
           | I'm pretty sure one of the biggest differences between C# and
           | Java is that in C# you can't assume everything is a
           | reference. A strict is passed by value.
        
             | fluoridation wrote:
             | No, strings are passed by reference, it's just that the
             | string type is immutable. Passing a string to a function
             | doesn't create a copy, but there's also no way for the
             | callee to modify the object that the caller points to.
             | 
             | It is true that C# has value types that are by default
             | passed by copy, though.
        
               | raincole wrote:
               | I mistyped struct as strict.
        
           | pjmlp wrote:
           | Java or Python would have been a better example, as that
           | isn't the case with C#.
        
           | lelanthran wrote:
           | > Use a decent IDE.
           | 
           | When I'm navigating code, the references aren't a problem,
           | because I am using an IDE of some sort, usually.
           | 
           | The complaint I made was about reading, not navigating, for
           | example when reviewing a PR.
        
             | uxp8u61q wrote:
             | Use a decent IDE that allows you to review PRs from within
             | the IDE.
        
         | pjmlp wrote:
         | I do a mouse over and move on.
         | 
         | Hardly any different from "var" parameters in plenty of
         | languages since the 1960's.
        
           | shadowgovt wrote:
           | It's cool that you're using development tools that
           | successfully parse the C++ you're working on.
           | 
           | I found that to be a rare circumstance in any complicated
           | code base. And once you lose that capacity, oh boy does this
           | language become terrible fast.
        
             | pjmlp wrote:
             | VS, QtCreator, Android Studio, KDevelop, C++ Builder,...are
             | some of the names that come to mind where this herculean
             | task is possible.
        
               | shadowgovt wrote:
               | And they are, indeed, very impressive. But the nature of
               | the language makes their task extremely difficult, and in
               | my experience they aren't reliable.
               | 
               | Besides the issue of C++ being just hard to compile
               | (global symbol mutation via the macro system, the way
               | templates instantiate, the compilation unit as the
               | fundamental element of compilation meaning any individual
               | compilation unit can require hundreds to thousands of
               | include files as input), the language is rife with
               | undefined behavior and assistive tooling is allowed to
               | crash on undefined behavior. So, yes, if your tooling
               | crashes it may indicate a problem with your code---but
               | how are you going to find it now that your tooling has
               | crashed?
               | 
               | We've had a couple of initiatives at my organization to
               | try and get tooling operational across the code base.
               | They haven't stuck. Developers keep having to fall back
               | to grep and pray.
               | 
               | I prefer working with languages where there are simply
               | fewer features and less undefined behavior. It makes the
               | tooling more reliable.
        
             | fluoridation wrote:
             | I recently worked in a fairly large Rust project that I
             | could simply not load into VS Code. rust-analyze would
             | allocate more and more memory until it eventually crashed.
             | I have never seen that with C++ code, besides that time I
             | was investigating how someone was using CImg and found out
             | that the CImg developers thought it was cute to make a
             | single 3 MB header, and that brought the debugger to its
             | knees. It still didn't crash, though!
             | 
             | So yeah, difficult-to-parse codebases are not an exclusive
             | feature of C++.
        
               | shadowgovt wrote:
               | Interesting. This is the first I've heard of a Rust
               | codebase too big to parse, but I'm not deeply surprised;
               | Rust tooling hasn't been hammered on for as many decades
               | as the C++ tooling.
               | 
               | I'm medium-level confident there's meat on the bones of
               | optimizing it to a point better than the C++ tools
               | because they made choices in the language that do some
               | amount of compartmentalization, so you don't get that
               | "one #define up in this header you've never seen changes
               | the meaning of symbols in every other file in this
               | compilation unit." Unless that _is_ possible and I simply
               | haven 't gotten deep enough into Rust to realize it yet.
        
               | fluoridation wrote:
               | Funny you should mention that. I'm pretty sure the reason
               | this project was so difficult to process was the (IMO)
               | abusive use of macros. I'm talking foo!( in one line, and
               | several hundred lines below was the closing parenthesis.
        
             | fsloth wrote:
             | I think you need to define what is a complicated code base
             | here.
             | 
             | Visual Studio works fine for millions of lines but I am
             | sure the difference in experience can vary wildly.
        
               | shadowgovt wrote:
               | Visual Studio crashes on the code base I maintain. It's a
               | monorepo using bazel to manage it, and between the sheer
               | number of includes compilation units take, the hundred
               | thousand plus compilation units themselves, and some
               | decisions made early on that are hostile to VS code
               | tooling because the original developers didn't use VS
               | code (such as hiding template implementations in .inl
               | files that are included in the corresponding .hh file),
               | VS code is functionally unusable for us.
               | 
               | In general, the company moves forward because most of the
               | same people who established the code base are still
               | working here and they just already know all the quirks
               | and can Intuit argument types, often because they wrote
               | all the classes in question.
        
               | ftrobro wrote:
               | Visual Studio != VS Code. I've always found Visual Studio
               | to be great at C++ so I'm interested in knowing which of
               | the two you meant?
        
               | shadowgovt wrote:
               | VSCode, to be clear. The fact that Microsoft released two
               | products in the same domain with almost the same name
               | consistently trips me up.
               | 
               | No, we don't spend money on code editors around here; I
               | meant VSCode.
        
               | fsloth wrote:
               | "No, we don't spend money on code editors around here; I
               | meant VSCode."
               | 
               | Not spending money in tools sounds like a very strange
               | economics for software development. If interviewing does
               | not scare you I warmly suggest finding a new employer.
        
               | TillE wrote:
               | Visual Studio Community is free to any company whose
               | annual revenue is under $1 million USD. It's fully
               | functional, unlike the ancient Express versions.
        
               | CyberDildonics wrote:
               | Visual Studio is not visual studio code. Also if you have
               | a hundred thousand compilation units, it might be time
               | for some shared libraries instead of one monolithic
               | program.
        
               | shadowgovt wrote:
               | Oh I completely agree, but I am a mere senior software
               | engineer and I have not yet figured out how to chisel
               | past the corporate cruft of the three folks in charge of
               | the codebase who believe monorepos are right because
               | Google is doing it, therefore it must be the best way to
               | do things.
               | 
               | Nevermind that I have some experience with _how_ Google
               | did it, and it turns out there 's a lot of ills you can
               | solve with a 25,000+-strong engineering force that you
               | can't in a smaller org. VSCode at Google never worked for
               | me either, but punching a novel symbol into company-wide
               | syntax-intelligent internal code search and glancing at
               | the first response, which was likely to be the right one
               | because it was a Google search engine so had well-tuned
               | signals for "things humans care about," always did.
               | 
               | They're not interested in how Google does it, merely that
               | they _do_ it so you can 't get fired for making an
               | architectural decision that worked for Google. Nor are
               | they really interested in solving the tooling issue
               | because it works for them; they just grep-and-pray.
               | They're very fast at it because it's all they know so
               | they've gotten quite good at it (when they have to do it
               | at all; often they just know the types because they wrote
               | the classes. ;) ).
        
               | fsloth wrote:
               | Ouch, my condolences.
        
       | diath wrote:
       | With regards to multiple return values in "By-Reference Parameter
       | Papercuts", you can make it more ergonomic with initializer list
       | and structured bindings without having to use std::make_tuple and
       | std::tie:                   auto get_multiple_values(const bool
       | condition)         -> std::tuple<bool, int>         {
       | if (condition) {                 return {true, 5};             }
       | return {false, 0};         }              void foo()         {
       | auto [status, value] = get_multiple_values(true);         }
        
         | lenkite wrote:
         | One can use structs too:                 auto foo() {
         | struct Result { int a; int b; int c; };          return
         | {1,2,3};       }       //....       auto [a,b,c] = foo();
        
         | NotCamelCase wrote:
         | What's the rationale for auto return type deduction and ->
         | declaration at the same time?
        
           | _proofs wrote:
           | herb has a talk where he describes its rationale.
           | 
           | it is known as sticky syntax in relation to auto (but
           | specifically when the type on the rhs is explicitly declared
           | and not abstracted away with a function signature)
           | 
           | it removes narrowing, inference, while keeping benefits of
           | auto related to inits etc.
           | 
           | it's a way of telling the compiler we want this type moving
           | forward and adheres to the convention of right-to-left
           | reading w.r.t. resource allocation semantics C++ programmers
           | are often accustomed to.
        
           | Chabsff wrote:
           | Consistency. The rationale is: "always use auto as the return
           | type, and add a postfix type if inference can't do the job
           | for a reason or another."
        
             | ninkendo wrote:
             | > add a postfix type if inference can't do the job for a
             | reason or another
             | 
             | Ehh... not putting a return type in at all is a bridge to
             | far IMO. Be friendly to the casual reader of the code, and
             | help them understand what the function returns. (Yes I know
             | IDE's can do this for you, but not everyone uses an IDE,
             | and not everyone is reading the code in the context of an
             | IDE... for instance code review, etc.)
        
               | unkulunkulu wrote:
               | Ehh... not using an IDE is a bridge too far IMO. Be
               | friendly to the casual writer of the code and help them
               | express themselves without having to repeat the obvious.
               | (Yes, I know, we write less time than read, but maybe
               | that is what is actually wrong with the tools we use?)
               | 
               | Wrote this for myself, I'm a vim user :)
        
               | gumby wrote:
               | Most of the time the compiler can deduce it so why bother
               | writing the return type?
        
               | sqeaky wrote:
               | So every reader should desuce it too?
               | 
               | Sometimes it deduces "wrong". (As in not the type the
               | coder was expecting, so really the dev was wrong and
               | missed a possible bug)
        
               | gumby wrote:
               | Those cases are the ones for which the explicit return
               | type was intended. And if the deduced type is
               | counterintuitive you could also use the explicit return
               | type (or a comment: there are good arguments for either
               | choice).
               | 
               | But if your argument is "this makes the reader do extra
               | work" then that's an argument against all uses of `auto`.
               | Now I do consider reader clarity a legitimate (and
               | important) design criterion (and argument in code
               | review), but I don't think it is reasonable in this case.
               | 
               | In fact I consider type deduction with auto to be an
               | important element of readability: it tells the reader not
               | to worry about the type and focus on the logic. Then the
               | cases when a type is explicitly specified it tells the
               | reader that it's worth paying attention.
        
               | sqeaky wrote:
               | Mandating or expecting tools for others is a problem.
               | Many code reviews are done in web settings, and that was
               | an example you ignored fromt he person you responded.
               | 
               | And you ignored any value that actually specifying the
               | type can have.
               | 
               | You also ignored the age old wisdom that code is written
               | once and read many times.
        
               | unkulunkulu wrote:
               | Why are you attacking me? I just put some motivation for
               | myself and maybe someone else to switch to an IDE. Is
               | this mandating tools thing a problem where you are
               | working? How did that affect you?
        
               | Chabsff wrote:
               | Ideally, functions should be clear at the call site
               | without a reader having to jump to the declaration to
               | understand what's going on, and you don't get the return
               | type there in the first place.
        
             | NotCamelCase wrote:
             | This looks like the worse of both worlds to me -- more
             | typing, less readability and you'd still need to manually
             | change the return type, if need be.
        
           | ninkendo wrote:
           | Not OP but I prefer trailing return type syntax if I were
           | designing a language from scratch... I sometimes wish C++11
           | would have added the `func` keyword as an alias for `auto` in
           | function declarations.
        
             | ooterness wrote:
             | With macros, anything is possible.
        
           | MereInterest wrote:
           | This is a stylistic difference, as there are some places
           | where the leading return type is invalid, but a trailing
           | return type using the -> is valid. For example, an
           | immediately invoked lambda expression in which some of the
           | returned values require type conversions.
           | 
           | For this reason, some prefer a "auto almost everywhere"
           | style, in which you preferentially use trailing return types.
        
           | gumby wrote:
           | Trailing return type declaration (-> type) was needed to
           | declare the return tyoe of a lambda -- no other way to get
           | that info into the grammar.
           | 
           | Then once that was opened some folks felt it would be better
           | to write their regular code that way too.
        
         | thecodedmessage wrote:
         | I'm just spoiled from being able to return and consume a tuple
         | in Rust without writing a standard library function or type
         | name _once_ :-) I 'm glad C++ has got it down to one though!
         | Now if only people will start using this instead of out
         | parameters (unlikely...)
        
       | antoineMoPa wrote:
       | For me, it's the fact that there is no standard package manager
       | for the language. Finding the right linker/include args is soooo
       | not user-friendly. Also, these `-I` and \-l` flags can differ
       | across platforms (and so can the installation procedure for these
       | libraries). Yesterday, I tried to run an old project of mine
       | developed on linux, but this time on a mac. First, the linker
       | complained it could not find -lglm, I had to search a bit to find
       | out that this is gl math. Then I searched this in homebrew, I
       | also looked at different package managers. The low quantity of
       | packages available surprised me. The most interesting package
       | manager looked like it required python. Can't the community build
       | a package manager in their own language? Anyway, at this point I
       | gave up for the day.
       | 
       | I love the language overall. Seems like standard & easier
       | management of dependencies and compiler flags would help it
       | thrive again.
        
         | ho_schi wrote:
         | I suggest changing the build tool. Meson improved C and C++ a
         | lot:
         | 
         | https://mesonbuild.com/
         | 
         | The dependency declaration and auto-detection is nice. But the
         | hidden extra is WrapDB, built-in package management (if
         | wanted):                   https://mesonbuild.com/Wrap-
         | dependency-system-manual.html
         | 
         | https://mesonbuild.com/Wrapdb-projects.html
        
           | boris wrote:
           | As the name suggests, this wraps the whatever build system
           | that the dependency is using rather than replacing it with
           | Meson. Say if you depend on Boost and Qt, you will end up
           | using both Boost Build and CMake in addition to Meson (most
           | likely there will be a couple of more build systems if you
           | are also building Boost's and Qt's own dependencies such as
           | ICU). This has two major drawbacks:
           | 
           | 1. The build in step-by-step rather than end-to-end. Meaning
           | that you first build Boost completely, then Qt, and then your
           | application. With an end-to-end build you would build
           | everything with a single build system invocation with the
           | build system "seeing" the entire build graph. In particular,
           | this would allow you to build only what's necessary and
           | faster.
           | 
           | 2. If something goes wrong in one of these dependencies, you
           | better be prepare to become an expert in whatever build
           | system it uses.
           | 
           | If you want a true Cargo-like experience in C++, a better
           | option would be build2, which replaces rather than wrapps the
           | build system (and, yes, it has Boost and Qt packages):
           | https://build2.org
           | 
           | For more details on what an end-to-end build can give you,
           | see: https://build2.org/faq.xhtml#why-package-managers
        
         | PaulDavisThe1st wrote:
         | You can't have an effective package manager for a compiled
         | language unless the package manager is actually a part of your
         | own build system.
         | 
         | The example I always use: you want to use libfftw ("Fastest
         | Fourier Transform in the West"). But the library can be
         | compiled with (at least) two options: use floats or doubles,
         | use threads or not. That creates (at least) four possible
         | variants of the final library.
         | 
         | The only way to address this (and IIUC, Cargo for Rust does
         | this) is to somehow have the pkg manager be a part of your
         | build system, so that you end up with a local copy of the
         | library, built as required.
        
           | ho_schi wrote:
           | https://mesonbuild.com/Wrapdb-projects.html
           | 
           | Meson did that. You can use system packages, define your own
           | dependency, use sub-projects (custom build option a, you
           | mentioned) or readily available from WrapDB.
        
         | pjc50 wrote:
         | > no standard package manager for the language
         | 
         | Kind of an uphill struggle this one, because there's no
         | standard _implementation_ of the language, nor is there a
         | standard build system. You have to deal with multiple vendors,
         | one of whom is Microsoft and many of whom are unwilling to
         | change rapidly.
        
         | xcdzvyn wrote:
         | Tbqh after figuring out make + pkg-config, it's kind of zen.
         | C/C++'s unique dependence on the system's package manager for
         | libraries is _weird_ , but I can't say I hate it.
        
       | usrnm wrote:
       | I don't know. I switched to go a year ago after 10+ years of C++
       | and, if anything, I realized that C++ was not so bad. Not bad at
       | all. It's old and overly complicated, but it's also very powerful
       | and even beautiful at times. Go is just bad, the runtime is
       | great, but it's attached to a slightly upgraded version of C.
       | It's not even much safer, I've seen more leaks in this year than
       | probably 5 years of C++ before that.
       | 
       | I know, the post is about C++ and Rust, but I'm tired of this
       | "C++ is abandonware, just switch to a modern language" narrative.
        
         | jprete wrote:
         | I think this is a fair view, but C++ has a lot of flaws. It's
         | good enough that it's very hard to justify switching away from
         | it, and it will be a good resume language for quite a long
         | time, but I would hesitate to start any new project in it.
        
         | thecodedmessage wrote:
         | I never recommended Go :-) I'd personally rather program C++
         | than Go, so I can see where you're coming from! That said, C++
         | is abandonware, switch to a modern language :-) :-)
        
         | fsloth wrote:
         | '"C++ is abandonware, just switch to a modern language"
         | narrative.'
         | 
         | I think this narrative is still valid. For the love of god, if
         | you don't need to use C++ for a greenfield project, don't. I
         | say this as a person who _needs_ to write C++ daily.
         | 
         | Otoh, if you _do_ need to use C++, it 's likely there are no
         | good alternatives, so unless you are Microsoft, Google or
         | Facebook ready to invest few billions to a new language
         | ecosystem, the most pragmatic approach is to, indeed, just use
         | C++.
        
           | GoblinSlayer wrote:
           | How much of C++ ecosystem Qt uses compared to the size of Qt
           | itself? Or Chrome?
        
           | Night_Thastus wrote:
           | As a C++ dev, I definitely disagree. C++ has a lot of
           | advantages, like an enormous pile of easily-usable existing C
           | and C++ code that you can hook in easily into any project.
           | 
           | Chances are if there's a problem to be solved, someone has
           | solved it in C or C++ and the code is out there already.
           | 
           | It also has a lot of great support in terms of toolchains.
           | Almost everything everywhere supports it out of the box, and
           | you have your pick of any tools you want.
           | 
           | It also works on fairly simple principles, so anyone with
           | experience in programming can work in it without learning a
           | completely new way of thinking. [Even if their code won't be
           | amazing at first]
        
             | fsloth wrote:
             | I must apologize if the below sounds acerbic. I've used C++
             | professionally close to two decades and developed a keen
             | feeling of everything that is bad in it. (Still need to use
             | it though).
             | 
             | In general I agree with Mark Russinovich. Use a garbage
             | colleted language, and if you really want a non-gc language
             | choose Rust (which I really should learn).
             | 
             | https://twitter.com/markrussinovich/status/1571995117233504
             | 2...
             | 
             | There are cases when one should choose C++, but those are
             | the exceptions. Graphics, computational geometry and so on
             | are strong reasons to use C++ still. But not everyone needs
             | that stuff.
             | 
             | For moving strings and numbers around in a stereotypical
             | business domain there are much better languages. C#.
             | Python. Etc.
             | 
             | "an enormous pile of easily-usable existing C and C++ code
             | that you can hook in easily into any project."
             | 
             | A lot of it is to help with the fact that the standard
             | library in C++ is anemic.
             | 
             | This _is_ one of the reasons though why one would want to
             | use C++. Some libraries exist only in C++, and yes, it 's a
             | good reason to use the language.
             | 
             | "It also has a lot of great support in terms of
             | toolchains."
             | 
             | CMake? VCPKG? Conan? MSBuild? Meson? Scons? Bazel? Make?
             | Xcode? The-android-thingy? How do you mix and match those?
             | 
             | All of those are bloody horrible. And their combinations to
             | the second power so. The only good thing in modern times is
             | that most open source projects have picked up CMake with a
             | fairly standard way of project configuration, which enables
             | actually to reuse other's people code with surprising ease.
             | 
             | The toolchains make a horrible language a doubly terrible.
             | 
             | "It also works on fairly simple principles, so anyone with
             | experience in programming can work in it without learning a
             | completely new way of thinking. [Even if their code won't
             | be amazing at first]"
             | 
             | Calling C++ simple is a new one to me. It's an _industrial
             | language_ that is not a _total failure_. Yes, people can
             | write programs with it. The problem is there are much safer
             | and better languages to write general programs, thus
             | selecting C++ is giving yourself an intentional handicap in
             | term of value created.
             | 
             | As an example of the complexity choosing C++ over something
             | like C# gives you -
             | 
             | To what extent is a C++ codebase cross compilable and
             | runnable in say MacOS after you develop it in Visual Studio
             | on Windows? That's right, you can't tell, the only way to
             | verify your standard C++ has the same runtime behaviour is
             | to run it on the said platform. And fix all the bugs. So to
             | actually write cross platform code, you need to have some
             | proficiency in all of the platforms to have any level of
             | guarantee that the code actually works in it's intended
             | runtime.
             | 
             | Thus, a C++ codebase needs to come with a warning label
             | telling every OS and processor it has been tested in. Most
             | popular libraries are tested and run in all of the relevant
             | platforms so this is not an issue for users. But I'm pretty
             | sure the number of platform specific fixes per codebase is
             | not trivial to make something work even remotely similarly.
        
               | Night_Thastus wrote:
               | >Make? VCPKG? Conan? MSBuild? Meson? Scons? Bazel? Make?
               | Xcode? The-android-thingy? How do you mix and match
               | those?
               | 
               | Almost everything I've seen is in Cmake, or can be easily
               | interacted with it.
               | 
               | But in general, just pick something you like and stick
               | with it. Sure, they're not perfect, but this list is
               | actually a point I was making - you have a huge array of
               | mature solutions out there already.
               | 
               | >Calling C++ simple is a new one to me. It's an
               | industrial language that is not a total failure. Yes,
               | people can write programs with it. The problem is there
               | are much safer and better languages to write general
               | programs, thus selecting C++ is giving yourself an
               | intentional handicap in term of value created.
               | 
               | C++ is a simple language with complex things you can
               | _choose_ to use. If you understand the basic principles
               | of loops, variables, functions, arrays, etc, you can
               | write in C++ right away. You can layer on greater
               | complexity with libraries and the STL, better patterns
               | etc as you go, but you can start simple. That 's a great
               | benefit to someone learning, IMO.
               | 
               | C++ is as complex as you make it. I prefer to write
               | fairly simple C++ personally and only break out the
               | harder parts when absolutely needed. For example, I
               | really don't like lambdas, so I just don't use them. And
               | I can still solve anything I need to without them.
        
               | wiseowise wrote:
               | > Almost everything I've seen is in Cmake, or can be
               | easily interacted with it. But in general, just pick
               | something you like and stick with it. Sure, they're not
               | perfect, but this list is actually a point I was making -
               | you have a huge array of mature solutions out there
               | already.
               | 
               | Installing library is
               | 
               | cargo add X npm install X implementation("X")
               | 
               | Show me how to import library in CMake. And then also
               | show me how to import library that doesn't have
               | CMakeLists.txt file.
               | 
               | > C++ is a simple language with complex things you can
               | choose to use. If you understand the basic principles of
               | loops, variables, functions, arrays, etc, you can write
               | in C++ right away. You can layer on greater complexity
               | with libraries and the STL, better patterns etc as you
               | go, but you can start simple. That's a great benefit to
               | someone learning, IMO. C++ is as complex as you make it.
               | I prefer to write fairly simple C++ personally and only
               | break out the harder parts when absolutely needed. For
               | example, I really don't like lambdas, so I just don't use
               | them. And I can still solve anything I need to without
               | them.
               | 
               | Good luck explaining to team that they can only use
               | subset of C++. Also good luck when advanced C++ dev
               | advocates for feature X and now whole org has to learn
               | about X and its drawbacks. Or when the dev leaves and
               | rest of team doesn't know how the hell it works.
               | 
               | Sounds to me like you're writing all of this from hobby
               | or hobby OSS project perspective. Which is completely
               | fine, but doesn't apply to orgs.
        
               | Night_Thastus wrote:
               | I'm writing this from the experiencing of maintaining
               | large legacy C++ projects as well as developing new ones.
               | I only do this for work, not as a hobby, nor anything OS.
               | 
               | >Show me how to import library in CMake. And then also
               | show me how to import library that doesn't have
               | CMakeLists.txt file.
               | 
               | I'm not sure how this is a gotcha. I'm not that familiar
               | with Cargo, but I think the equivalent would just be
               | something like:
               | 
               | TARGET_LINK_LIBRARIES(myProgramName Eigen3::Eigen)
               | 
               | in cmake. Not very hard.
               | 
               | Also...why does it matter if there's a cmakelists.txt
               | file? How is that a bad thing?
               | 
               | >Good luck explaining to team that they can only use
               | subset of C++
               | 
               | You learn as you go, like literally any other language.
               | You don't need to understand every single line and
               | function call in a program to make changes or add things
               | to it. As you come across something you need to modify,
               | you learn a bit about the features being used.
               | 
               | Now, if you have a real go-getter on the team who has
               | covered the entire project in the most advanced features
               | used in the most obscure ways, sure, it's going to suck
               | for anyone just starting. But that's a problem again, in
               | literally any language. And not a fault of C++, frankly.
        
               | wiseowise wrote:
               | > I'm not sure how this is a gotcha. I'm not that
               | familiar with Cargo, but I think the equivalent would
               | just be something like:
               | 
               | > TARGET_LINK_LIBRARIES(myProgramName Eigen3::Eigen)
               | 
               | Include how the library gets in your build, not how you
               | link it.
               | 
               | > Also...why does it matter if there's a cmakelists.txt
               | file? How is that a bad thing?
               | 
               | Maybe because it gets dramatically harder than having
               | `cargo install x`? Or did they solve it recently where
               | any autotools/meson/whatever gets pulled into project
               | without any blood sacrifices to pass all compiler flags
               | properly?
               | 
               | > You learn as you go, like literally any other language.
               | You don't need to understand every single line and
               | function call in a program to make changes or add things
               | to it. As you come across something you need to modify,
               | you learn a bit about the features being used.
               | 
               | How can you make you changes to something you don't
               | understand and make sure that it still works? Also, this
               | is absolute nightmare from perspective of team and org
               | cohesion.
               | 
               | > But that's a problem again, in literally any language.
               | And not a fault of C++, frankly.
               | 
               | No, it's not. Aside from maybe Scala, I can't think of
               | any mainstream modern programming language where you have
               | to actively create your own sublanguage to make it
               | bearable.
        
               | Night_Thastus wrote:
               | >Include how the library gets in your build, not how you
               | link it.
               | 
               | Kind of depends. If you're in a Linux environment or
               | something like MSYS, you'd just need the relevant Eigen
               | package (in MSYS: mingw-w64-eigen3), which then you can
               | link against. You could build it from source instead I
               | suppose, and it has its own cmakelists which you just add
               | as a subproject if you want to go that route.
               | 
               | This isn't rocket science. Could it be easier? Sure. But
               | it's not terrible either.
               | 
               | >How can you make you changes to something you don't
               | understand and make sure that it still works? Also, this
               | is absolute nightmare from perspective of team and org
               | cohesion.
               | 
               | You learn about how _the part you 're modifying_ works.
               | Ask for some assistance from the team members. Read the
               | docs. Look up anything from libraries/stdlib that you
               | don't understand, go into the debugger and watch as the
               | state changes. Build the knowledge gradually.
               | 
               | The next thing won't be as difficult. Don't try to take
               | in the entire project at once, that's an overload of
               | information for any non-trivial project. Again, that's
               | true in literally any language.
        
             | wiseowise wrote:
             | Everything that you've listed also applies to any modern
             | mainstream language.
        
               | Night_Thastus wrote:
               | Rust doesn't have those decades worth of libraries and
               | tool development baked into it. Nor does any fairly new
               | language.
        
               | wiseowise wrote:
               | Java has incredible tooling, far more advanced than
               | whatever exists in C++ world. If you're looking
               | specifically for "fairly new" language then Kotlin is
               | your bet. Tooling is almost on par with Java. Can utilize
               | ALL of Java ecosystem on JVM. Multiplatform (JVM, JS,
               | native).
               | 
               | C#, TS if you're looking outside of JVM.
        
         | stackedinserter wrote:
         | Interesting, it's quite the opposite to me. I've been in C++
         | programming since TurboVision times (don't miss them), built
         | many projects in C++, but Golang feels like fresh air to me, at
         | least for server applications.
         | 
         | Probably it's mostly about runtime/libraries than language
         | itself.
         | 
         | Aside of embedded applications and hardcore number crunching
         | (core part of it) I can't find a single use for C++ in 2023.
        
           | linhns wrote:
           | Games come to mind and anything that needs performance.
        
             | BlarfMcFlarf wrote:
             | Starting from scratch, Rust is a fine option there, but it
             | lacks publicly available platform integrations, or any
             | engines as powerful as Unreal. It's to be seen if that
             | momentum advantage can be overcome anytime soon.
        
         | pjmlp wrote:
         | Yeah, Go is a better C alternative in terms of language space.
         | 
         | If one cares about language design beyond the ideas explored in
         | Oberon and Limbo, not so much.
        
           | uxp8u61q wrote:
           | Can I use Go to program my STM32 MCU?
        
           | zare_st wrote:
           | If you mean Go is closer to C than Rust is to C, I guess
           | that's correct. But neither are alternatives to C.
        
             | pjmlp wrote:
             | They certainly are, outside the religious UNIX and embedded
             | groups that cannot grasp anything beyond C.
        
               | zare_st wrote:
               | Then those outside groups should make a software
               | foundation that's attractive enough to replace UNIX.
               | 
               | I see Debian today tinkering with two additional kernels
               | - FreeBSD and Hurd, and both are written in C.
               | 
               | Until some kernel 100% written in Rust/Go/modern C++
               | starts being attractive for OS integration there is
               | absolutely no point in claiming it's possible and even a
               | better choice.
               | 
               | C has lost a lot of ground since 30 years ago,
               | rightfully. User applications with GUI used to be written
               | in something like C89. Games too. It's some people that
               | just cannot grasp that it and its ecosystem have a huge
               | merit over 'alternatives' when it comes to the ground it
               | retained and still holds today.
        
               | BlarfMcFlarf wrote:
               | Doing a top down rewrite of an OS is an absurd task, so
               | regrardless of merit, C is going to retain its place
               | there by momentum. If it's going to be replaced at all,
               | it will be incrementally, and until recently, the powers
               | that be have kept other languages out of that space (and
               | for good reason, since multiple languages interacting
               | have complexity costs, again regardless of individual
               | language merits).
               | 
               | But we have started seeing just a bit of Rust support in
               | Linux now...
        
               | lelanthran wrote:
               | You keep going on as if C programmers only use it because
               | they are not as smart as you, who punts C++ all the time.
               | 
               | I wonder if you are unable to understand that for some
               | people and some problems, having fewer footguns is a
               | benefit.
        
       | nwallin wrote:
       | > Side note: Someone brought up structured bindings in a comment,
       | as if this fixed the issue. Structured bindings are a great
       | example of the half-way fixes that proponents of modern C++ love
       | to cite. Structured bindings help some, but if you think they
       | make returning by tuple ergonomic, you're mistaken. You still
       | need to write std::pair or std::make_tuple in the function return
       | statement.
       | 
       | No you don't. A tuple needs to be declared once. Then everything
       | else is sugared.                   #include <string>
       | #include <tuple>              using Foo = std::tuple<int,
       | std::string, float>;              Foo foo(int a, const char* b,
       | double c)         {             return {a,b,c};         }
       | Foo bar(long);              double baz(long x) {
       | const auto [a, b, c] = bar(x);             return c;         }
       | 
       | godbolt: https://godbolt.org/z/exdsb4hqb
       | 
       | Note that the return part of this (foo()) worked as far back as
       | C++11. Only the structured bindings on the receiving side (baz)
       | requires C++17.
        
         | thecodedmessage wrote:
         | Thanks! It's still not as ergonomic as full first class
         | support, but I did have the situation in my head as worse than
         | it was. This has been fixed :)
        
       | andsmedeiros wrote:
       | I just don't use out parameters and avoid bare references
       | altogether, const lvalue and rvalue references express
       | idiomatically whether a parameter is to be copied or moved.
       | Structured binding makes it trivial to consume returned tuples,
       | so I don't find this a big issue.
        
         | thecodedmessage wrote:
         | You still have to write boilerplate to return them, though :-(
         | But I agree it's better :-) Unfortunately, I'm on C++11 for
         | this project, will be on C++03 for others :-(
        
       | meindnoch wrote:
       | >There are so many features I miss from Rust, not only the
       | obvious safety features, or even primarily those, but also
       | features that C++ could easily add, like sum types (called enums
       | in Rust), or first-class support for tuples.
       | 
       | Stopped reading here. std::variant and std::tuple.
        
         | thecodedmessage wrote:
         | What part of "first-class" don't you understand??
         | 
         | ETA: std::variant is one of the least ergonomic user interfaces
         | I've ever seen. std::tuple seems great until you realize that
         | most PLs that support tuples just do "(,)"s for them, in
         | creating, binding, and naming the type. C++'s versions are just
         | unacceptable, and structured bindings only help on the binding
         | side.
         | 
         | First-class support for these is important. There's no reason
         | they should be in the standard library rather than built into
         | the language itself, besides historical mistakes.
        
           | nwallin wrote:
           | > std::tuple seems great until you realize that most PLs that
           | support tuples just do "(,)"s for them, in creating, binding,
           | and naming the type.
           | 
           | This C++ code compiles and does what a casual reader would
           | expect it to do:                   #include <string>
           | #include <tuple>              using Foo = std::tuple<int,
           | std::string, float>;              Foo foo(int a, const char*
           | b, double c)         {           return {a,b,c};         }
           | 
           | godbolt: https://godbolt.org/z/Koa4zYT99
           | 
           | I gotta be honest, this interface is exactly what I would
           | like a tuple API to look like. I _want_ to declare my tuple
           | as a `tuple`, I _want_ to give it a friendly name, and I
           | _want_ to explicitly declare the types in the tuple, and I
           | want to all of that at compile time. This API is exactly the
           | verbosity that I want; I would be unhappy with either more or
           | less boilerplate than this.
           | 
           | > First-class support for these is important.
           | 
           | Agree to disagree.
        
         | jprete wrote:
         | std::variant is really cumbersome to use.
         | 
         | When people talk about first-class support for tuples,
         | std::tuple does not fit that bill.
        
         | gwbas1c wrote:
         | > It can be done, but the calls to std::tie and std::make_tuple
         | are long-winded and distracting
         | 
         | Edit: It's really important to read criticisms of your favorite
         | language. Often, it can help you find a better language for the
         | task you're trying to accomplish.
        
           | mempko wrote:
           | You don't need to use tie and make_tuple. Another person
           | posted a nice example.
        
       | j1elo wrote:
       | Back in 2015 I read _Effective Modern C++_ by Scott Meyers [1],
       | which is his latest entry in the saga of  "Effective C++" books.
       | It covers all the new stuff that got into the language with C++11
       | and C++14, and shows how modern C++ can be written.
       | 
       | My feeling during the read, and my definite conclusion
       | afterwards, is that I didn't read a collection of new nice
       | additions to the language, but a list of minefields and new fancy
       | ways to crash your program. That there is no chapter which
       | doesn't end up being a list of gotchas. Seemingly every new
       | feature was added as an extension of the language's reputation of
       | being a shotgun that loves your feet. "Oh, now we have lambdas!
       | here are the 32 ways you can fail while using them, and the
       | language will do nothing to help you prevent".
       | 
       |  _Effective Modern C++_ is a very well written book and very
       | instructive. Its flaws are none of the book or writer, but of the
       | language itself.
       | 
       | [1]: https://www.aristeia.com/books.html
        
         | bowsamic wrote:
         | Yeah, Effective Modern C++ has always read like a list of
         | warnings. C++ is kind of like Python in that the design of the
         | language does not steer you into making good, idiomatic
         | decisions. No one is going to guess most of the stuff that is
         | taught in that book without reading it first from working with
         | the language or reading other people's code, and really a full
         | list of such things for C++ could be many hundreds or even
         | thousands.
         | 
         | I also agree that the new features seem to embrace and extend
         | the "footgun-ness" of C++, rather than attempt to fix it.
        
           | PaulDavisThe1st wrote:
           | > the design of the language does not steer you into making
           | good, idiomatic decisions.
           | 
           | That's much easier to do when the tasks that the language is
           | suitable for are bounded in some way.
           | 
           | There are not many common idioms that are going to be shared
           | (beyong libstdc++) between a realtime audio processing
           | application and a data visualization backend for the web and
           | an embedded system control app and a payroll processing
           | backend.
        
             | bowsamic wrote:
             | I don't agree. I can think of many languages that obfuscate
             | their idioms that have bounded applicability. Obviously I
             | already mentioned Python, which is obviously bounded to
             | less performant applications (unless you call C code), but
             | another one is SQL. For example, Azure SQL is extremely
             | slow when using sub queries compared to CTEs, but the
             | language does not make this apparent in its design.
             | 
             | An example of a language with a smaller domain which makes
             | its idioms obvious through its design is Ruby.
             | 
             | An example of a high-performance language that can be used
             | for all those things that makes its idioms obvious through
             | its design is Rust. Another would be, imo, C.
             | 
             | > There are not many common idioms that are going to be
             | shared (beyong libstdc++) between a realtime audio
             | processing application and a data visualization backend for
             | the web and an embedded system control app and a payroll
             | processing backend.
             | 
             | I also just generally have to disagree with it, but you
             | seem to be using a different definition of idiom to me.
             | From what you said here, you seem to take idiom to be
             | synonymous with "design decision". For me, an idiom is a
             | specific semantic construct, possibly associated with
             | specific syntax, that achieves a specific basic programming
             | task. For example, the idiomatic way to loop with an index
             | in Python is to use enumerate.
             | 
             | It's merely the way that most people do the basic things,
             | in a way that is actually good to do. C++ is infamous for a
             | huge number of people using the language dangerously, to
             | the point where there are effectively no idioms outside of
             | specialist communities, and to find them out you need to
             | read books. A significant amount of the basic design of the
             | language needs to be ignored for most purposes.
        
               | PaulDavisThe1st wrote:
               | One of the main places I see idioms in other languages is
               | in dealing with containers (and there are some very nice
               | ones). Python in particular, but others too. Slicing,
               | splicing, iterating, mutating, map-reduce etc. etc.
               | 
               | While I wouldn't say no to having such idioms available
               | in C++ as language constructs, it is important that C++
               | continues to provide the lowest level that it can for
               | such tasks (e.g. pointers into arrays or other buffers),
               | and probably the level right above that (libstdc++).
               | 
               | Similarly for threads (or more generally, parallel and
               | asynchronous programming) - some other languages make
               | available some really nice idioms for expressing these
               | things, but again it is important for performance and
               | control reasons that C++ continues to provide the current
               | lowest level of access for this (basically a wrapper
               | around pthreads). While things like promises, futures and
               | coprocessing are really useful, they are often
               | inappropriate for highly performant code.
        
               | bowsamic wrote:
               | If rust can do it so well, I see no remaining argument
               | about performance and control. It was long believed that
               | C++ had to be bad in this way because such languages must
               | be. Rust was the counter example that ended this myth
        
               | PaulDavisThe1st wrote:
               | That's not clear to me. The examples I've seen from
               | anywhere that Rust has to deal with hardware suggest that
               | you have to throw out most of the safety guarantees of
               | the language, and this applies also to dealing with OS-
               | provided abstractions of hardware (like MMU-mapped
               | registers and mmap-ed memory regions).
               | 
               | I don't have the impression that Rust's container idioms
               | match those of, say, Python, but I may just not know Rust
               | well enough.
        
               | bowsamic wrote:
               | You're mistaken then, using unsafe does not mean throwing
               | away the safety guarantees. This is a common
               | misconception though, even among Rust users
        
               | PaulDavisThe1st wrote:
               | OK, let me put it differently: you cannot reason about
               | safety in those circumstances in the way you can when
               | "unsafe" has not been used. And since reasoning about
               | safety (or perhaps more accurately, not needing to) is
               | one of the raison d'etres for Rust, that's a bit of a
               | hit. Not huge, but for those of us close to metal of some
               | sort, it's a bit of a hit.
        
               | bowsamic wrote:
               | Yes, you can reason about safety in just the same way, as
               | long as a safe API is built around the unsafe usage.
               | That's why you can have safe OpenGL bindings even though
               | the underlying raw C calls are unsafe
               | 
               | E.g. https://github.com/glium/glium
               | 
               | Rust would be useless if you couldn't have safety if you
               | had any unsafe at any point. That's the whole point of
               | rust: you can build safe interfaces to unsafe actions
        
               | shadowgovt wrote:
               | One gap in my knowledge here is how well Rust can take
               | advantage of per-architecture optimizations that can only
               | be seen in the large.
               | 
               | One of the sources of complexity in the C++ I work with
               | is that a lot of it is using templates to try to maximize
               | the size of individual expressions. Consider, for
               | example, the Eigen library
               | (https://eigen.tuxfamily.org/index.php), which gains most
               | of its magic in using compile-time template
               | specialization to re-group logical operations so that the
               | compiler can realize "Oh, this is really looping over
               | dozens of memory cells to do the same operation... I
               | should compile it using SSE instructions." I haven't seen
               | yet whether / how Rust pulls off similar tricks or
               | whether doing so in Rust requires a developer to write
               | n-ary asm bindings, which would actually make Rust less
               | expressive than C++ if the goal is maximizing performance
               | with minimum code.
               | 
               | (Of course, the flip side of that is that there's a lot
               | of Eigen that's unsafe behavior; hold the library
               | _slightly_ wrong, and you 're operating on deallocated
               | memory. Oops. ;) ).
        
           | DougBTX wrote:
           | Which book would be the equivalent to an Effective Modern
           | Python?
        
             | mixedmath wrote:
             | Probably _Fluent Python_ comes the closest, I would say.
        
             | bowsamic wrote:
             | I'm not aware of one, since Python changes quite a bit more
             | quickly than C++.
        
             | sn9 wrote:
             | _Effective Python_ probably: https://effectivepython.com/
        
         | raincole wrote:
         | No kidding. Effective Modern C++ was what got me to actually
         | start learning Rust.
         | 
         | That being said I feel Rust has a lot of paper cuts that slow
         | people down too. Perhaps the silver lining is that they are
         | more like barricades than minefields.
        
           | PennRobotics wrote:
           | Rust would be so much easier for me if it didn't feel like
           | every. single. keyword. was different than my daily drivers,
           | Python and C---from type names to standard library functions,
           | containers to punctuation. Even "void" isn't safe (hehe pun)
           | from this keyword purge.
           | 
           | Beginners, count your blessings. You don't need to unlearn
           | other languages before learning Rust.
           | 
           | I sincerely believe the only keywords common to Rust and C
           | are a few control flow keywords (for, if, while; but not
           | switch, goto, or do-while) and "bool"---the latter only
           | because of C99 and stdbool.h
        
             | silvestrov wrote:
             | readability is so underrated.
             | 
             | I think readability is a major part why java was hugely
             | successful: C programmers could read java code and
             | immediately understand what the code did and able to make
             | minor modifications to the code.
             | 
             | This is very difficult with Rust code. Rust "learned" too
             | much from Perl in this regard.
        
               | raincole wrote:
               | > C programmers could read java code and immediately
               | understand
               | 
               | Uh... maybe? But Rust is more like a replacement to
               | C/C++. So a better question is that whether C#/Java
               | programmers could read C/C++ code and immediately
               | understand those & and *.
        
               | silvestrov wrote:
               | No. C existed long time before java, so the natural
               | career progression was for C programmers to become java
               | programmers.
               | 
               | It is not at all as easy for C programmers to become Rust
               | programmers as you need to learn a lot about Rust before
               | you are able to understand the Rust code.
        
               | Const-me wrote:
               | > whether C#/Java programmers could read C/C++ code and
               | immediately understand those & and *.
               | 
               | Just like Rust, C# has an unsafe superset of the
               | language. The meaning of all these * in unsafe C# exactly
               | matches C.
        
               | tialaramex wrote:
               | Do you think so ? Maybe I've been immersed in Rust for
               | too long but it doesn't seem difficult to read to me.
               | 
               | Here's Barry Revzin, a C++ programmer, explaining what he
               | wants with a Rust example
               | 
               | https://brevzin.github.io/c++/2020/12/01/tag-
               | invoke/#this-is...
               | 
               | Now like I said, I'm a Rust programmer, so of course I
               | understand PartialEq's definition there, but, is it
               | _really_ hard to see what 's going on as a C programmer?
               | There's a lot of magic you don't have, you don't have
               | traits, or references, or the Self type or the self
               | keyword, or really any of the mechanics here, and yet it
               | seems pretty obvious what's going on, doesn't it?
               | 
               | I literally don't remember any unpleasant surprises of
               | this form when learning Rust. Actually mostly the
               | opposite, especially for != I thought, from experience
               | with other languages like Java, C++ or Python, OK
               | obviously at some point the other shoe drops and they
               | show me that I need a deep comparison feature. Nope,
               | Rust's comparison operators are for comparing things, it
               | isn't used to ask about some surface programmer
               | implementation detail like the address of an object in
               | memory unless you specifically ask for that. If Goose
               | implements PartialEq, so that we can ask if one Goose is
               | the same as another Goose, then HashSet<Goose> also
               | implements PartialEq, so we can ask if this set of geese
               | is the same as another, and HashMap<Goose,String>
               | likewise, so we can ask if these two maps from geese to
               | strings are mapping the same geese to the same strings.
        
               | fauigerzigerk wrote:
               | _> I literally don't remember any unpleasant surprises of
               | this form when learning Rust_
               | 
               | I can't say I find Rust's pattern matching rules
               | particularly pleasant. The rules are convoluted and
               | arbitrary
               | 
               | https://doc.rust-lang.org/stable/reference/patterns.html
               | 
               | Also, it has often been surprising to me how code that
               | looks like it's entirely read-only will try to move
               | stuff.
               | 
               | E.g, this works:                   let numbers = [1, 2,
               | 3];              for n in numbers {
               | println!("{}", n);         }              for n in
               | numbers {             println!("{}", n);         }
               | 
               | This doesn't work:                   let numbers2 =
               | vec![1, 2, 3];              for n in numbers2 {
               | println!("{}", n);         }              for n in
               | numbers2 {             println!("{}", n);         }
               | 
               | I understand why that is and I know how to fix it, but I
               | don't like it. There is a long list of things in Rust
               | that I find understandable but also unpleasant. In that
               | sense it really is a worthy successor to C++. Smart
               | people making unpleasant decisions for good reasons.
        
               | tialaramex wrote:
               | I think your mental model is wrong if you thought a
               | consuming operation like IntoIterator::into_iter is read
               | only.
               | 
               | The reason the first one works is that [T; N] is Copy if
               | T is Copy, and i32 is Copy so therefore [i32; 3] is Copy.
               | So it's actually consuming _two_ arrays, each of those
               | for loops consumes the array to make an iterator, but
               | since it 's Copy it just leaves a perfectly good copy of
               | the array behind in the variable. The compiler can see
               | what we're doing here and won't pointlessly duplicate the
               | array (presumably) but that's conceptually what's
               | happening.
               | 
               | The second one doesn't work because the Vec<i32> isn't
               | Copy, so, we consume it and now it's gone, when we reach
               | the second for loop the variable numbers2 is gone
               | already.
        
               | fauigerzigerk wrote:
               | As I said, I understand what's going on here. I just
               | don't like it.
        
               | eddd-ddde wrote:
               | Being read only and move semantics are orthogonal, you
               | can read and write with or without moving.
               | 
               | So you shouldn't make assumptions whether something will
               | be moved or not based on read only preconditions.
        
               | fauigerzigerk wrote:
               | Moving out of a variable changes it's state. That's not
               | read-only.
        
               | eddd-ddde wrote:
               | There is a difference between a variable and a value. The
               | value is definitely read only or you would need a mut
               | modifier. The variable binding changes in a similar way
               | to shadowing it with a new 'let' on the same name.
        
               | fauigerzigerk wrote:
               | This would imply that the variable can get its value back
               | once the new binding is out of scope. But that is not the
               | case as this doesn't compile:                   let s1 =
               | String::from("abc");              {             let s2 =
               | s1;         }              println!("{}", s1); //borrow
               | of moved value: `s1`
               | 
               | But I do understand what you're getting at. In a sense, a
               | move is not a runtime concept at all. The compiler simply
               | considers the variable as no longer readable unless and
               | until a new value is actually written to it.
        
               | PennRobotics wrote:
               | I find Rust extremely unreadable and can detail exactly
               | how:
               | 
               | I know ripgrep is written in Rust and works really well
               | and "does one thing", so I'll go to its repo (
               | https://github.com/BurntSushi/ripgrep ) and read through
               | a bit. First, I'm looking for a "src" folder, but that
               | does not exist, so right off the bat I am a little
               | uncomfortable. Nothing in the root folder looks like it
               | would be the source. What are "complete" and "scripts"
               | and "pkg" folders? Because those (in that order) would be
               | what I check.
               | 
               | "complete" looks like command line completion. The
               | "scripts" directory has one file, "copy-examples". "pkg"
               | contains folders "brew" and "windows". Still lost...
               | 
               | I thought "crates" was like modules for Python, so I
               | think that would only contain dependencies and stay out
               | of there.
               | 
               | Finally, I relent and open "build.rs" which I suspect is
               | something like "build.ninja" and I can figure out which
               | node has source files. Stupid me. "build.rs" IS the
               | source file.
               | 
               | Oh. No, actually, it's not all of ripgrep, in fact. It's
               | the tip of the iceberg. The source is in
               | crates/core/app.rs, and build does manpage compilation,
               | registers shell completions, etc.
               | 
               | So, line one. I don't know what the return value
               | App<'static, 'static> is. I understand it's an App
               | object, and the tick (no idea what it's called; this is
               | starting to feel like a fracture mechanics lecture) I
               | know has to do with ownership. It's tough to see how
               | there are two subtypes in App<> when the assignment of
               | app has ten functions that assign attributes. I can
               | certainly READ what is being assigned to the App object.
               | I wouldn't be able to WRITE any of this so far.
               | Unimportant, it's not the meat and potatoes of ripgrep.
               | 
               | Now I'm scrolling, looking for something cool to analyze
               | and run across Vec<&'static str>. Okay, I thought tick
               | was ownership, but I also thought ampersand was
               | ownership-related. Maybe one is mutability? (This is like
               | knowing how to play music really well but now learning a
               | totally foreign instrument.)
               | 
               | Skimming, it looks like this whole file is the argc/argv
               | parser. I'll look for something interesting in main.rs,
               | search.rs, and subject.rs (the last because it's an
               | unusual name).                   struct Config {
               | strip_dot_prefix: bool,         }
               | 
               | Well. I can read that! Next line...
               | impl Default for Config {             ...
               | 
               | ...not that. It's probably something like a class method.
               | 
               | As I'm reading through, it feels like a LOT of kicking
               | the can down the road and verbosity. There's a function
               | binary_detection_explicit(self, detection) that just
               | assigns detection to self.config.binary_explicit and
               | returns self. binary_detection_implicit() does basically
               | the same with a different assignment member. getters and
               | setters, roadblocking readability since the dawn of
               | computer science... (a few lines later, the function
               | has_match() returns the value of has_match. ugh. This is
               | why I prefer crudely written C to textbook C++.)
               | 
               | I see .unwrap() in a line. This tripped me up during the
               | tutorial; still no idea what it does. Sure I can look it
               | up, but there are a hundred other terms to learn: unwind,
               | map, Result (OH! The two App return types are probably Ok
               | and Err ... maybe?), convert, concat, const, continue,
               | ...
               | 
               | subject.rs, btw, can probably be written in three lines
               | of really dense Python or ten lines of really well-
               | written Python with a few other lines of Doxygen.
               | Instead, it's about 90 lines of Rust with another 50
               | lines of comments.
               | 
               | Finally, I find something noteworthy.
               | fn search(...) ...             fn iter(...) ...
               | for subject in subjects {                     searched =
               | true;                     let search_result = match
               | searcher.search(&subject) {                         ....
               | 
               | This is all extremely readable and just feels like a
               | standard queue in any language. But then there are lines
               | like:                   if let Some(ref mut stats) =
               | stats {             *stats +=
               | search_result.stats().unwrap();         }
               | 
               | I give up. Are you assigning the variable stats to
               | itself? by reference but mutable? If Rust doesn't have
               | pointers, what is *stats? unwrap() is no longer the most
               | confusing part of this.
               | 
               | -----
               | 
               | Postface, I'm originally a mechanical engineer who
               | eventually got roped into writing driver software, so C
               | is my comfort zone and I don't need anything much more
               | complicated than "set bits here, read registers, use a
               | 40-year-old communication protocol, handle errors".
               | 
               | There's simply no easy analog between Rust and Python ...
               | or Rust and C ... or Rust and SQL ... or Rust and
               | PRACTICE ... or Rust and any other language I've learned.
               | I understand a lot of the CWE Top 25 software errors are
               | mitigated by Rust and it's not just a theoretically
               | correct but unusable language, but there hasn't been a
               | strong reason to learn it, and the learning curve is made
               | steeper by preconceived notions of every keyword.
               | 
               | -----
               | 
               | I forgot to compare with grep.c
               | 
               | Extremely readable. I know right away which part is args,
               | where memory allocation happens, where file handling
               | occurs, and then the "hard part"---where the magic
               | happens---is the last page on my monitor. I can just
               | stare at that and mentally deconstruct until pieces fall
               | into place... Look up regcomp()? check. Look up
               | grep_tree()? check. Read the kernel.org discussion on why
               | grep_tree() was written and how it relates to
               | ensure_full_index(), which explains some of the other
               | variables.
               | 
               | The whole thing sits nicely in a few pages with terse but
               | clear comments (unlike this post) and looks familiar even
               | though I've never seen the source before.
               | 
               | -----
               | 
               | Last edit. I tried just now to refresh on .unwrap().
               | https://doc.rust-lang.org/rust-by-
               | example/error/option_unwra...
               | 
               | Only because of the comments did I realize that calling
               | drink.unwrap() will panic (throw an error?) if that
               | argument is empty. So, unwrap is just the "if (... ==
               | nullptr) { return ...; }" of Rust, it seems.
               | 
               | But it's somehow an alternative to .expect() and has its
               | own alternatives,
               | unwrap_or/unwrap_or_else/unwrap_or_default, and it also
               | seems optional. And my time in the rabbit hole is over;
               | my children need to go to bed.
        
               | rcxdude wrote:
               | Do you expect there to be an easy analog between any two
               | random languages? Even two languages as similar as
               | javascript and python I would not expect to immediately
               | jump in and understand something written in one by just
               | kinda guessing as to what something I didn't recognise
               | meant, or assuming that just because something is written
               | the same as in one language it means the same as in the
               | other.
        
               | PennRobotics wrote:
               | Yep. Honestly, I don't find it too difficult to parse new
               | languages even if they aren't C-like or Python-like (or
               | Matlab-like or BASIC-like). I don't know Java, but I can
               | read the Falstad circuit simulator source. It's just...
               | very recognizable code patterns---almost a dialect of
               | C++.
               | 
               | I'm not a database expert, but I became comfortable with
               | SQL this past year so I could better understand how my
               | company's office software is structured. I can cobble
               | together enough Javascript to create an own online sheet
               | music book using abcjs, a directory full of ABC files,
               | and jquery. I was able to barge through enough C# to
               | write a crude 2D game during a game hackathon in college.
               | 
               | In fact, AFTER learning Python, a lot of C became easier
               | because I knew HOW a data structure should behave, which
               | then influenced how I would create it from structs and
               | functions.
               | 
               | However.
               | 
               | I get hung up on Kotlin. Kotlin reminds me a lot of Rust
               | in its syntax, and Android has the same habit of renaming
               | every single concept.
               | 
               | In general, the more keywords and syntaxes two languages
               | have in common, the easier to read. (In SQL's case, I
               | think the relative lack of keywords and rigid query
               | structure made this a nonissue.)
               | 
               | So, mixed bag.
        
               | inferiorhuman wrote:
               | Well there it is. Rust is difficult not because it's
               | inherently difficult but because it's not the spitting
               | image of the languages you've learned and you're not
               | interested learning (which is totally fine). Things like
               | putting dependencies (instead of program source) in
               | 'modules' seem intuitive not because they're inherently
               | intuitive or common but because you've taken the time to
               | learn Python.
               | 
               | IMO ripgrep is a poor example of project layout both
               | because it does "non-standard" things and because it
               | encompasses more than the C-based grep you're comparing
               | it to. A simple project will almost always have its
               | source in a directory named "src", and a project with
               | multiple subprojects (like ripgrep) will typically not
               | shove them into a "crates" directory.
               | 
               | Compared to GNU grep, the ripgrep project serves as the
               | repository for a bunch of libraries as well as the
               | ripgrep binary. So there's already a lot more moving
               | parts than the name "ripgrep" might otherwise suggest.
               | I don't know Java, but I can read the Falstad circuit
               | simulator source.
               | 
               | If you can read Java, I'm surprised impl blocks threw you
               | for a loop as the first comparison that comes to mind is
               | that with Java interfaces. A quick look at the Falstead
               | simulator suggests it's using a pretty small subset of
               | the Java language (e.g. no generics, interfaces, or
               | exceptions) which would certainly make it seem a lot like
               | C. Not all Java is like that.
               | 
               | & and *? Akin to reference and dereference in C, and
               | given that you can overload both the comparison to C++
               | seems pretty apt.
               | 
               | In any case, for more direct explanations the API
               | reference is a better place to look. Two short sentences
               | explain what unwrap and explain do, and what the
               | differences are.
        
               | diarrhea wrote:
               | Not sure I follow. How did you ever read or learn
               | anything more than pseudo code or, at best, Python? Any
               | language at all will have hurdles like those.
        
               | burntsushi wrote:
               | Author of ripgrep here.
               | 
               | ripgrep is way more abstracted than most greps because
               | ripgrep splits a whole bunch of its functionality into
               | crates. The idea being that others can then re-use the
               | reasonably sophisticated infrastructure that ripgrep uses
               | to write their own bespoke grep tools. ripgrep internals
               | are more like a bare-bones and under-developed grep
               | framework. If you go back to the initial version of
               | ripgrep (0.2.0), you'll probably find it smaller and
               | significantly easier to read. There's a lot less
               | abstraction. FWIW, I generally regard my abstraction
               | attempts here to be a partial failure. The hit to code
               | readability has been quite large. I also fucked up at
               | least one abstraction boundary. On the other hand, it has
               | enabled people to maintain very small patches to make
               | ripgrep work with other regex engines.[1] (And of course,
               | ripgrep uses the abstraction to make it work with both
               | Rust's regex crate and PCRE2.)
               | 
               | I wouldn't call Rust an easy language to learn. It could
               | be easier depending on your background. For example, if
               | you have an ML/Haskell and C++ background, then Rust will
               | probably introduce very few things you haven't seen
               | before (that being the borrow checker). But if your
               | background is, say, Python, Java and C, then there are
               | going to be oodles of things in Rust that will be novel
               | to you. That basic vocabulary will be more difficult to
               | acquire.
               | 
               | I'm somewhat surprised you feel confident enough in your
               | understanding of Rust to declare you could replace 50
               | lines with 2 or 3 lines in Python though. Meh.
               | 
               | > getters and setters, roadblocking readability since the
               | dawn of computer science...
               | 
               | They are tools of encapsulation. I don't treat them as
               | boiler-plate. I treat them as things that I use when I
               | care about encapsulation. And when I don't care about
               | encapsulation, I don't use them. My personal style is to
               | lean heavily on encapsulation.
               | 
               | [1]: https://sr.ht/~pierrenn/ripgrep/
        
               | BlarfMcFlarf wrote:
               | This is really helpful to understanding what you mean. If
               | you would be interested, I could go through all that and
               | explain each piece. But the normal answer of "spend a few
               | hours reading the rust book, it will be faster then
               | trying to muddle through" is fine, and misses the point.
               | 
               | You are absolutely right. The foreign-ness you are seeing
               | is the ML side of Rust's roots. Rebinding, restructuring,
               | small wrapper types, and of course all the functional
               | programming bits and bobs that people coming from, say,
               | Haskell might see as being quaintly simple, but arcane to
               | anyone else.
        
               | thenewwazoo wrote:
               | I've been leaning Spanish for about the last six years.
               | I'm reasonably effective at it. Not fluent, but I can
               | speak and understand it well enough that I often don't
               | notice how easy it's gotten for me. A while back, I tried
               | reading something written in German and was actually
               | surprised at how little I understood. Clearly I can
               | understand not-English, why was German so hard? I spent a
               | little while trying to parse out each word, guess common
               | roots, vaguely remember the little bits of German grammar
               | I've absorbed over the years, and so on. It was
               | illuminating. I could not just force myself to understand
               | German by relying on my success learning Spanish. (This
               | is a real story, btw. I really did this.)
               | 
               | This comment reminds me very strongly of myself, trying
               | to read German. You know C, why shouldn't you be able to
               | understand Rust? But they're not the same thing, and they
               | communicate different things in different ways. There are
               | concepts that simply do not map directly. Someone could
               | _very_ easily write the exact same post from the
               | perspective of a Rust programmer starting with C. Where
               | is the equivalent to Cargo.toml? What is this Makefile
               | thing, and why doesn 't it look like a configuration
               | file? Etc. No amount of just forcing yourself to try is
               | going to provide clarity unless you already understand
               | the idioms and structure at play.
               | 
               | With that said, I have written embedded Rust and it is an
               | _amazingly_ freeing experience. Writing C right now feels
               | like trying to balance a knife on my finger. With Rust I
               | can just get work done. I strongly recommend you spend
               | the time to learn it. Even if it doesn 't convince you
               | that C's days are numbered, it will make you a better
               | programmer by training you to think about safety and
               | correctness up front.
        
               | Gibbon1 wrote:
               | > Clearly I can understand not-English, why was German so
               | hard?
               | 
               | I'm reminded of a Mexican friends mother, she and her
               | aunts went to Europe. They loved loved Italy. Partly
               | because if an Italian spoke slow and clearly they could
               | understand what the person was saying.
               | 
               | My experience with C# for instance was it was really easy
               | to pickup. Because snippets of code are generally C like
               | and procedural.
               | 
               | Heard the above about said about go. If you know Java and
               | JavaScript you probably know go already.
               | 
               | I found python not too hard because I knew perl sort of.
               | Mostly enough experience with it to avoid it.
               | 
               | I suspect if you know C++ and a ML language Rust is a
               | breath of fresh air. But if you don't it's not. It's
               | gross. And very very slow iteration times.
        
               | PennRobotics wrote:
               | My mom was a Spanish teacher in a reasonably hispanic
               | part of Florida. I'm now a permanent resident of Germany.
               | Your example resonates.
               | 
               | -----
               | 
               | I tried Embedded Rust about 4 to 6 years ago and found
               | the experience underwhelming, as I was installing a TON
               | of packages just to get Blinky and seeming to bypass a
               | lot of standard Rust (no_std, no_main, use panic_halt as
               | _, everything GPIO is unsafe, etc). The last straw was
               | that imported Cargo packages had their absolute path
               | somehow hardcoded in the symbol table, so my debugger
               | would try to load, like... C:\Users\jacob\stm32-rust-
               | thingy\app\solve.rs (and fail, because I'm not jacob and
               | don't have his source code)
               | 
               | I'll give it another shot. I'm sure the "many eyes"
               | principle (and better/more tutorials in the past few
               | years) make the process easier now than then.
        
               | cesarb wrote:
               | > I spent a little while trying to parse out each word,
               | guess common roots, vaguely remember the little bits of
               | German grammar I've absorbed over the years, and so on.
               | 
               | And German has a particularly special trap for "trying to
               | parse out each word": trennbare Verben (separable verbs).
               | Unless you know enough of the grammar to know when a
               | piece of the verb has been unceremoniously punted to the
               | end of the sentence, trying to use a dictionary to find
               | the meaning of the verb of a sentence can be an exercise
               | in futility.
        
               | pjc50 wrote:
               | > C programmers could read java code
               | 
               | This isn't some abstract "readability", though, this is
               | "how close are your langauges in the evolutionary tree".
               | Where ALGOL is the proto-indo-european of quite a lot of
               | langauges, but not all.
        
             | mr_00ff00 wrote:
             | I feel like this is a bit unfair. Rust takes most of its
             | names from C++ or OCaml.
             | 
             | You just are exposed to different languages than what was
             | used as the base.
        
             | tialaramex wrote:
             | Conveniently we can actually compare:
             | 
             | https://doc.rust-lang.org/reference/keywords.html
             | https://en.cppreference.com/w/cpp/keyword
             | 
             | And they're even both provided in (mostly) alphabetical
             | order, which makes this easier for humans
             | 
             | Rust doesn't reserve type names as keywords, which seems
             | like an eccentric choice in C++ (e.g. wchar_t is a
             | _keyword_!) although perhaps given how fraught parsing is
             | in C++ they had no choice.
             | 
             | The common keywords are: break, const, continue, else,
             | enum, extern, false, for, if, return, static, struct, true,
             | union and while.
             | 
             | true and false are just boolean values, although given C++
             | like many languages considers "false" to be true, and 0 to
             | be false, I guess it's a surprise they bothered making
             | these keywords.
             | 
             | const, enum, union, static, and struct are about types and
             | values. The choice to have "const" mean "immutable" not
             | "constant" in C++ is hilarious, yeah that's going to cause
             | wider issues in how your programmers understand software. I
             | think C++ enum is rather sad, here's a whole kind of type
             | and it's basically abandoned, it's not allowed to have
             | features the other types have, for no discernible reason.
             | Rust's enum really shows how much opportunity was wasted
             | there.
             | 
             | break, continue, else, for, if, return and while are indeed
             | control flow keywords, although what they do in Rust varies
             | quite a lot from C++. Rust's for is a for-each, it consumes
             | an Iterator (literally, it's a syntax sugar for a loop
             | which uses IntoInterator::into_iter and breaks when the
             | iterator is exhausted) and Rust's break can break-label-
             | value, which is a violently different thing than the C++
             | break even though the obvious beginner syntax looks
             | identical.
             | 
             | try, do and virtual are reserved in Rust but are not
             | currently used (in stable Rust) to mean anything, we can
             | expect that try is likely to some day be used for a related
             | but different purpose to its cousin in C++.
        
               | hardlianotion wrote:
               | What's the difference between constant and immutable?
        
               | tialaramex wrote:
               | An example of a constant would be a literal, such as
               | "Hello, World!" or 4 or false
               | 
               | You can see that not only can't you change these values,
               | it doesn't even mean anything to speak about doing so,
               | they're not variables which _have_ values, they 're the
               | values themselves.
               | 
               | Whereas an example of an immutable variable would be the
               | std::vector we're being asked to check the length of in a
               | call to size() - this certainly is a variable, somebody
               | _else_ can change it, but we must not.
               | 
               | If we name a constant in Rust like { const TIMEOUT: u32 =
               | 1234; }, we're not saying we want a variable with this
               | name TIMEOUT and then we mustn't change it - instead we
               | are giving a name to this arbitrary constant TIMEOUT (and
               | it has to be constant, Rust won't let us make constants
               | unless they actually are) and then any time we say that
               | name, we get the same value, not the same variable, there
               | is no variable, the same value, the constant one, as if
               | we'd written it here.
               | 
               | So for example the C++ trick where you "cast away" the
               | const-ness doesn't make any sense in Rust because those
               | aren't immutable variables, we can't even _try_ to make
               | them mutable, they 're not variables. It's not Undefined
               | Behaviour, it's gibberish, even C++ won't let you write
               | false = true; for example, that's just clearly nonsense.
        
               | layer8 wrote:
               | "Const" doesn't mean immutable in C++, in my book. It
               | means unmodifiable or read-only. Immutable means the
               | value won't ever change, whereas unmodifiable means that
               | the value can't be modified via that type (but it might
               | be modified some other way).
        
               | raincole wrote:
               | Compile time vs runtime.
        
               | GolDDranks wrote:
               | Constant is determined at compile time. It's constant
               | over the every run of the same binary.
               | 
               | Immutable is immutable for some specific span of time. In
               | a simple case, like with Python immutable objects, it is
               | constructed once, and after that, it never changes. With
               | Rust, it's also possible for an object to be immutable
               | while a reference to it exists and is passed to some
               | function, but after that function call returns and the
               | reference ceases existing, immutability ends.
        
               | estebank wrote:
               | To add to this, the following is valid:
               | fn take_foo(x: Foo) -> Foo {             let mut x = x;
               | x.field = 0;             x         }
               | 
               | The ownership semantics ensure that because there's a
               | single owner of Foo at all times, it is always safe to
               | make its binding mutable. This is different than trying
               | to turn a &Foo into a &mut Foo, that's not allowed by
               | safe Rust (and an undefined behavior minefield in unsafe
               | Rust).
               | 
               | For constants, you can think of them as a "stamp",
               | whatever the value within the const gets inlined in the
               | usage place in your code, every time. Immutability is
               | about whether the value can be changed. To stretch the
               | analogy, you can stamp a pattern on paper (const) but
               | later doodle over it (mutability).
        
               | layer8 wrote:
               | > wchar_t is a _keyword_!
               | 
               | In C, wchar_t is a typedef, which in C++ would cause the
               | problem that overloads could non-portably conflict with
               | one of the builtin integer types. That's why wchar_t was
               | made a fundamental type, and thus a keyword like all the
               | other integer types.
        
             | nayuki wrote:
             | Void is not a good example for you to point out. Rust's
             | interpretation is correct: The unit type () is what most
             | functions should return. The void type has no valid values
             | at all, which means it cannot be returned. It is other
             | languages that are wrong for using the term void. The
             | actual void in Rust is called never: https://doc.rust-
             | lang.org/std/primitive.never.html .
        
               | saurik wrote:
               | > The void type has no valid values at all, which means
               | it cannot be returned.
               | 
               | In C/C++, any value can be cast to void, which should be
               | sensible as any pointer can decay to a pointer to void.
               | Thereby, the Wikipedia definition of the void type even
               | says it is like a unit type, and the page for the unit
               | type notes 1) the void keyword simulates a unit type and
               | 2) that--and I think this is very important, to get into
               | the mindset of this term--that the actual unit object
               | type in Java is named Void.
               | 
               | It does really really suck that you can't declare a
               | variable of type void and that some compilers decided the
               | size was 1 for long enough that the size is now undefined
               | (instead of 0), as these mistakes lead to a ton of
               | ridiculous code duplication in C++ templates. (This would
               | be the first thing on my list of things to fix if I were
               | allowed to make any arbitrary breaking change to C++.)
               | 
               | But like, it seems very wrong to claim it has no values
               | at all: in C++, if you have a function that returns void,
               | you can even call another function that returns void and
               | use the return keyword oh whatever you consider it to
               | have returned. It clearly has some kind of value in many
               | contexts--and, despite having a size that is undefined
               | (and potentially 1), that value carries no state--so
               | there is only one possible value: it is merely the term
               | people use for unit in this kind of language.
               | 
               | (To be clear, though: I am not at all claiming that Rust
               | is wrong for using () or unit or whatever, as there is a
               | ton of history of using such in other safe academic
               | languages and it certainly doesn't confuse someone like
               | me. I do, though, think that its mission isn't well
               | served by not at least trying to look as much like the
               | specific languages that need to be replaced--C and
               | C++--as possible.)
        
           | KineticLensman wrote:
           | > Perhaps the silver lining is that they are more like
           | barricades then minefields
           | 
           | As a Rust newby I think they are more like achievements that
           | unlock new capabilities.
        
             | moonchrome wrote:
             | There's a name for this feeling [1]
             | 
             | [1] https://en.wikipedia.org/wiki/Stockholm_syndrome
        
               | Verdex wrote:
               | From your wikipedia link:
               | 
               | "and, in fact, it is a "contested illness" due to doubts
               | about the legitimacy of the condition."
               | 
               | AND
               | 
               | "It was noted that in this case, however, the police were
               | perceived to have acted with little care for the
               | hostages' safety,[6] providing an alternative reason for
               | their unwillingness to testify."
               | 
               | Somehow appropriate considering this thread is about how
               | someone's bad experience with C++ has driven them into
               | learning rust.
        
               | spoiler wrote:
               | So learning any new skill is a manifestation of Stockholm
               | syndrome?
        
           | linhns wrote:
           | I love Rust but it's not the time for it yet, especially with
           | that enormous build time. That's the biggest paper cut.
        
           | FpUser wrote:
           | >"...they are more like barricades than minefields"
           | 
           | This is a nice one ;)
        
         | npalli wrote:
         | Scott Meyers was the go-to reference for "expert" C++ knowledge
         | for over twenty years, he retired after his C++14 books came
         | out and a mere two years later he couldn't even keep up with
         | the language to make technical corrections.
         | 
         |  _C++ is a large, intricate language with features that
         | interact in complex and subtle ways, and I no longer trust
         | myself to keep all the relevant facts in mind. As a result, all
         | I can do is thank you for your bug report, because I no longer
         | plan to update my books to incorporate technical corrections.
         | Lacking the ability to fairly evaluate whether a bug report is
         | valid, I think this is the only responsible course of action._
         | 
         | https://scottmeyers.blogspot.com/2018/09/the-errata-evaluati...
        
           | googlryas wrote:
           | Heck, even C++ is getting out of hand for Herb Sutter. He
           | might say "ISO C++ is the best tool in the world today to
           | write the programs I want and need", but then he also says
           | "My goal (with cppfront) is to explore whether there's a way
           | we can evolve C++ itself to become 10x simpler, safer, and
           | more toolable."
           | 
           | https://github.com/hsutter/cppfront
        
             | darknavi wrote:
             | As a C++ fan his cppfront talk was excellent. I'd love to
             | see this work get wider adoption.
        
             | cassepipe wrote:
             | I am really sold on cppfront but I have not had the
             | occasion to use it yet.
             | 
             | How usable is it ? Is it closer to a a Proof of concept or
             | a finished tool ?
        
         | rewmie wrote:
         | > My feeling during the read, and my definite conclusion
         | afterwards, is that I didn't read a collection of new nice
         | additions to the language, but a list of minefields and new
         | fancy ways to crash your program.
         | 
         | I don't think that's a reasonable take on both the book and
         | C++11/c++14. `Effective Modern C++` is not an intro tutorial,
         | but a book dedicated to trivial and nontrivial gotchas, none of
         | which leads your program to crash. Worst case scenario you just
         | get a compiler error of your own doing, and that's it. I mean,
         | the book covers stuff like the importance of using nullptr
         | instead of NULL, why braced initialization is nice, why and how
         | smart pointers should be used to implement a pimpl, etc. This
         | is hardly mine field territory, and it makes absolutely no
         | sense at all to use these tidbits of all things to justify
         | switching to an entirely new programming language.
        
         | limaoscarjuliet wrote:
         | Agreed 100% The last time C++ "made sense" to me was 1998
         | reading and practicing Bjarne Strostrup's C++ book. It was
         | better C then and a lot of these improvements made sense.
        
       | nayuki wrote:
       | I agree with the blog post, also having learned C++ followed by
       | Rust. Only in the context of saner modern languages like Rust did
       | I see C++'s footguns in retrospect.
       | 
       | Probably my top footgun for C++ is implicit copy construction by
       | default. Every class gets a copy constructor by default unless it
       | opts out. When a copy constructor exists, it can be used
       | implicitly and silently for casting and other contexts.
       | Implicitly copying big objects like std::string or std::vector
       | can be a huge, silent waste of time - whereas in Rust you must
       | explicitly call `.clone()`.
       | 
       | > const is not the default
       | 
       | Agreed with this footgun. The old C and C++ ways are to be
       | permissive by default, to be strict after adding more words.
       | 
       | As another example, top-level variables and functions are
       | exported by default unless prefixed with `static`. Whereas in
       | Java, Rust, etc., you need `public`/`pub` to export, nothing to
       | keep private.
       | 
       | Another huge footgun is the collection of all the undefined
       | behaviors from integer overflow to null dereferencing to creating
       | out-of-bounds pointers to buffer overflows.
        
       | 1n3nt0r wrote:
       | I've used C++ from its inception (when it was cfront C++ -> C
       | translator) to modern C++, not to mention many other languages
       | over the years. Totally agree with the author that C++ manages to
       | accumulate many bad decisions over time, leaving a legacy of
       | overly complex or under-powered features.
       | 
       | I suspect a certain laziness in not tackling some of the harder
       | problems in the compiler, for example: type inference, improving
       | the grammar to disambiguate new keywords from variables (for
       | example co_yield). Many features could have been improved with
       | more effort and willingness to tackle hard problems.
       | 
       | Perhaps if Sean Baxter's Circle (C++ language extensions) gains
       | traction, there will be hope to modernize C++ syntax and
       | features.
        
       | [deleted]
        
       | RcouF1uZ4gsC wrote:
       | > In C++11, there is no ergonomic way to do a lambda capture by
       | move - which is usually how I want to capture variables into a
       | closure
       | 
       | > It is unergonomic to return multiple values by tuple in C++. It
       | can be done, but the calls to std::tie and std::make_tuple are
       | long-winded and distracting, not to mention that you'll be
       | writing unidiomatically, which is always bad for people who are
       | reading and debugging your code.
       | 
       | C++14 allows you to move capture in lambdas. C++17 has structured
       | bindings that help with returning multiple values.
       | 
       | If you are claiming to an expert in C++ talking about paper cuts,
       | at least be aware of things that have happened in C++ in the last
       | decade.
        
         | fsloth wrote:
         | Let the holy wars begin.
         | 
         | "Lambda capture" sounds like "my favourite way to juggle
         | chainsaws".
         | 
         | The correct answer to "I don't like lambda syntax..." is not
         | "learn lambda syntax" but "do not use lambdas unless you really
         | need them, and then think twice".
         | 
         | Honestly, C++ is a massive footgun. Write the most simple,
         | inelegant C++ you possibly can. The coming generations who need
         | to maintain your code for the next 200 years will be thankfull
         | for it.
         | 
         | Don't pretend C++ is something more elegant like Ocaml that the
         | compiler sugars you into believing. It's not! It's a horrible
         | pile of ... things ... piled .. on top of .. things .. on top
         | of ...(etc).
         | 
         | And when something goes sideways at any point in that pile of
         | templated tower of abstractions, the compiler will gift you
         | with an outpout that is closer to a mix of toenail clippings
         | and dog poo than an output from an industrial level programming
         | tool. After which you need a heavy drink and wish there was a
         | better language out there. And then you realize there isn't,
         | for the specific task, and you adjust by reducing the
         | complexity of the abstractions you use and prefer to have a
         | codebase that has more lines of code and fever chances of
         | irritating the compiler.
        
           | gpderetta wrote:
           | I don't care if you do not like the lambda syntax (there is
           | plenty of syntax of C++ that I dislike), but lambdas are now
           | a pretty core feature of the language, so you better learn it
           | or switch to another language.
        
             | fsloth wrote:
             | Strong disagree.
             | 
             | It's not about the syntax, it's about understanding the
             | resource management model lambdas add on top of the
             | existing mess.
             | 
             | There is no good reason to use lambdas except
             | parametrization of stl algorithms.
             | 
             | Don't use them as part of api design. The only thing you
             | gain is one more level of complexity on reasoning resource
             | management.
             | 
             | Yes, to be specific, one should know how they work, and
             | _then_ not use them.
             | 
             | This is in the context of C++ being a minefield of
             | practically infinite complexity, in which the only way to
             | survive is to pick the language subset you know you are
             | able to handle, and stick with it.
             | 
             | Of course, every party picks their own subset :)
        
               | AnimalMuppet wrote:
               | > There is no good reason to use lambdas except
               | parametrization of stl algorithms.
               | 
               | IIRC, there are usually several alternate ways to
               | parameterize STL algorithms. If lambdas don't add enough
               | value to be worth the additional complexity in other
               | places, then they don't as STL arguments either.
        
               | fluoridation wrote:
               | Would you mind being a little less vague about what's so
               | bad about lambdas?
        
               | fsloth wrote:
               | Lambda-the-cs-concept is ofc beautiful.
               | 
               | Lambda-the-c++-language-feature is not. The purpose of
               | programming languages in an industrial context is to make
               | implementations of programs simple. When they utilize
               | complex abstractions, the abstraction should simplify
               | overall architecture. The way to measure the
               | 'simplification' is how much reasoning a programmer needs
               | to do to understand what their code does in the context
               | of the language constraints and runtime.
               | 
               | In the context of C++ this includes runtime memory use
               | and execution speed. The latter requirement means C++
               | code must be such that it's amenable to performance
               | analysis and when hotspots arise, one should have an
               | obvious strategy how to continue.
               | 
               | C++ Lambdas make this reasoning more complex (in my
               | experience).
               | 
               | So the main problem is the added complexity to reasoning
               | about runtime behaviour, resource use and performance.
               | 
               | And this is not complexity that would add value by
               | removing that complexity from some other area of the
               | program.
               | 
               | Lambdas in C++ start by looking cute and innocent. Then,
               | when the first problems arise, you realize that by using
               | a lambda instead of just an in-place class to capture the
               | context, which you can reason using the same bysantine
               | set of rules, you actually need to grok a whole new set
               | of rules on top of that:
               | https://en.cppreference.com/w/cpp/language/lambda
               | 
               | That's a huge mental cost in general what is a saving of
               | few lines of code. Hence, don't use lambdas in api
               | design.
               | 
               | The only way I guess to realize why the lambdas are in
               | such bad taste is to use a programming language where
               | they are part of the core language syntax, like python,
               | scheme, F#... where you can use them without each time
               | double checking that you are not accidentally shooting
               | your foot off.
        
               | fluoridation wrote:
               | >Then, when the first problems arise, you realize that by
               | using a lambda instead of just an in-place class to
               | capture the context, which you can reason using the same
               | bysantine set of rules, you actually need to grok a whole
               | new set of rules on top of that
               | 
               | No, I don't agree at all. Unless the callee accepts an
               | std::function, passing a lambda is exactly equivalent to
               | passing an ad hoc functor. There's no problem you'll run
               | into with one that you wouldn't have run into with the
               | other.
        
               | gpderetta wrote:
               | Lambdas are just very thin sugar over local classes. I'm
               | not sure what do you find so complex.
        
               | fsloth wrote:
               | Exactly, but using a different set of syntax, plus a new
               | ruleset of capture semantics. The capture syntax
               | especially is a minefield. And are the runtime guarantees
               | _exactly_ the same as that of a local class instance?
               | 
               | The question is - what does this extra complexity buy us?
               | Is it worth it?
        
               | fluoridation wrote:
               | >The capture syntax especially is a minefield.
               | 
               | Functors are no easier to use. Either you initialize
               | captured values through a constructor or by manually
               | setting each member. In both cases you have to name the
               | same values several times and ensure that you're doing it
               | correctly. With lambdas, if capture by value or by
               | reference is enough for everything you need, the code is
               | _much_ more succinct and readable. It seriously reduces
               | useless, error-prone boilerplate.
               | 
               | >And are the runtime guarantees exactly the same as that
               | of a local class instance?
               | 
               | Yes.
               | 
               | >The question is - what does this extra complexity buy
               | us? Is it worth it?
               | 
               | It buys us more readable and straightforward code. Yeah,
               | I'd say it's worth it. I haven't written a functor in
               | years.
        
         | thecodedmessage wrote:
         | I said "In C++11" because I am aware that it has been fixed
         | later. But I'm stuck in C++11 for platform reasons and so I get
         | to complain about its problems too. But of course, it is
         | because I am aware that this has been fixed -- crappily -- that
         | I qualified it. I'll add more qualifications for you :-)
         | 
         | And I have now addressed structured bindings in the article
         | itself for what they are: a halfway solution, unacceptable to
         | anyone used to real tuples.
        
         | hardware2win wrote:
         | >C++14 allows you to move capture in lambdas.
         | 
         | Your quote says cpp11 and youre arguing with 14? Wtf?
        
           | leni536 wrote:
           | The limitations of C++11 shouldn't really be relevant today.
        
             | [deleted]
        
             | Night_Thastus wrote:
             | Hahahaha. Oh that's a good one.
             | 
             | Source: Someone who was, until recently, limited to C++03.
             | Luckily we're up to 17 now, but occasionally 11 limitations
             | creep in.
        
               | thecodedmessage wrote:
               | Author of the blog post here. I am limited to C++11 for
               | this, and will be limited to C++03 for some related
               | projects. I did say C++11 in that comment, as I am of
               | course _aware_ that C++ has  "fixed" that issue, but I'm
               | still entitled to my gripes. I find it odd that someone
               | claimed I was not aware the issue has been fixed in later
               | versions, when I specifically said a version. People seem
               | to be very motivated to assume I'm wrong about C++...
               | because otherwise they'd have to admit that their
               | favorite language has flaws.
               | 
               | In any case, I revised that section to explain how even
               | the "fixed" version in C++14 is still a paper-cut, and
               | make the section hopefully flow a little better.
               | 
               | ETA: I replied to the right comment. I'm agreeing with
               | you :-)
        
               | Night_Thastus wrote:
               | Did you reply to the wrong comment?
               | 
               | I was just saying that being stuck on older versions is
               | more common that most people think.
        
       | FpUser wrote:
       | All valid points. I agree that C++ as a whole is insanely big and
       | complex and I can't even dream nor that I want to know about
       | every nook and cranny.
       | 
       | None of this however precludes me for writing well behaved
       | software using subset of modern C++ that I actually need. The
       | good thing is that when I need some esoteric feature that will
       | help me to to this and that - it is always there and is quickly
       | searchable thanks the the Internet.
       | 
       | I prefer wide array of C++ choices especially when it comes to
       | supporting various paradigms and techniques to a stricter set in
       | Rust for example.
       | 
       | I do see that real C++ experts (I am not the one) can get
       | frustrated with the language but I think we have different goals
       | in general. Being a programmer I do run my own business and I
       | believe ROI wise C++ is more beneficial for my situation (I do
       | use multiple languages when needed / requested by clients).
        
         | Night_Thastus wrote:
         | Exactly. Just use what you need. Only use the big hammers when
         | absolutely needed. That's the power of C++ IMO. Lots of big
         | hammers out there, but lots of smaller ones for when you don't!
        
       | kajaktum wrote:
       | I started writing C++ to do some JSON at work and it is
       | absolutely horrible piece of shit language (even with nlohmann's
       | and simdjson). I cannot believe people live like this.
       | 
       | between figuring out CMake scripts, std::optional being basically
       | useless. I never feel done writing code because I don't feel like
       | I have:
       | 
       | 1. Checked all the exceptions
       | 
       | 2. Checked all the places where my std::optional instance is null
       | (the checks doesn't propagate their information)
       | 
       | 3. Checked that I am not doing any implicit conversion.
       | 
       | 4. Really understand what happens at overflow?
       | 
       | 5. Really understand if my code is even UB free?
       | 
       | 6. Debugging is a mess, I can't step through code because the
       | smartass compiler removes an entire segment of code even in debug
       | mode.
       | 
       | 7. I wish this garbage is written in C instead. At least its
       | honest that its primitive and sinful, unlike this LARPING high
       | level language.
       | 
       | Only 2 things need to be true for this language to be permanently
       | horrible: Hyrum's Law and backward compability. If it is possible
       | to write bad code, then there will be bad code. Simple as.
       | Doesn't matter how many guidelines you write or how many times
       | you need to "teach" programmers or how many times you said people
       | just need to be "better mkayyy?"
        
         | jalino23 wrote:
         | i was thinking of learning cpp for webgpu, this is discouraging
        
           | adwn wrote:
           | Consider learning Rust instead, and use wgpu [1].
           | 
           | [1] https://github.com/gfx-rs/wgpu/
        
           | kajaktum wrote:
           | You can do it using whatever you want. I hope I don't
           | discourage you from discovering C++. You might enjoy it, you
           | might not, who cares. Go do it and make up your own opinion.
        
           | pjc50 wrote:
           | Good. C++ is discouraging. It combines huge complexity with
           | readily available footguns, and the prevailing attitude is
           | that all developers must be able to write perfect code with a
           | full understanding of the language. Otherwise any security
           | holes or runtime crashes are their fault.
        
             | fsloth wrote:
             | You are absolutely correct.
             | 
             | Computer graphics, though, is one of the few sane reasons
             | left to learn C++.
        
         | fsloth wrote:
         | "I started writing C++ to do some JSON at work and it is
         | absolutely horrible piece of shit language"
         | 
         | Congratulations! That is the correct answer. Welcome to the
         | language.
         | 
         | If you don't need to use C++, don't.
         | 
         | You need C++ for stuff like computer graphics, computational
         | geometry, plausibly scientific computing, games and so on.
         | 
         | "6. Debugging is a mess, I can't step through code because the
         | smartass compiler removes an entire segment of code even in
         | debug mode."
         | 
         | Visual Studio is brilliant compared to anything else. If you
         | really need to use C++, I would warmly suggest it.
        
       ___________________________________________________________________
       (page generated 2023-08-28 23:02 UTC)