Hacker News new | past | comments | ask | show | jobs | submit login
What it feels like when Rust saves your bacon (smallcultfollowing.com)
197 points by lukastyrychtr on June 24, 2022 | hide | past | favorite | 184 comments



This article is super overcomplicated. All it had to say was that rust tells you when you keep reference to on stack variable after it goes out of scope. Context provided adds nothing.

I must say - as someone who doesn't use rust - I haven't had this type of issue in years, and when I did it wasn't hard to debug. You get corrupted data, set data breakpoint and in the provided example you will see it being modified by unrelated operations on stack. From there there is only one conclusion. Authors reactions seems to be a bit exaggerated.


Yeah no. In my experience, when several people commit to a common C/C++ codebase this kind of issue become really exhausting when it happens more than once, and the symptoms may be so subtle it's a bitch to debug.

Rust lowers your mental load. You spend more time being creative and way less time debugging "obvious" (or not) mechanical problems (reference not-on-stack-anymore variables, use-after-free, concurrent write access and all kind of compiler undefined behavior). That why garbage collected languages are so successful (they let you concentrate on the business logic) and for the first time it's available in a system language.

Everything that can be done by your computer should be done by your computer. You should leave your precious brain cells available for the important stuff.


I work in probably what is considered one of the least "safe" languages: C++

The issues that Rust is supposed to help with are simply not what we spent time on. All the bugs reported are pretty much exclusively root caused to "business logic".

From recent time I can recall only one that was a programming mistake and not architecture/business logic related. It was a missing break in a switch that already had some fallthroughs so it didn't look incorrect at a glance.

I do understand what Rust is supposed to provide but in practice it's simply an extremely minor source of bugs.


I see many C++ programmers say stuff like this, and maybe it is the case that studios exist which can do C++ without mistakes related to memory management or sigils and so on, but I also observe that:

1. High quality teams like the Linux kernel team and PostgreSQL, do periodically have serious security bugs that are things Rust would have caught.

2. I see sometimes C++ instructors making the same claims you do and then spending half of a lesson tracking down a memory mistake, (Casey Muratori) or in the same month a tweet from a game engine developer saying they don't really see the value of garbage collection and then tweeting about how they spent 48 hours tracking down a memory mistake. (of course, for a game engine that is what you have to do sometimes!)

There are however valid questions, like if Rust slows down your development say, 5%, would you get more net safety from spending 5% more time testing/fuzzing c++ code instead? etc.


> High quality teams like the Linux kernel team and PostgreSQL

Neither of those code bases are C++, which significantly dilutes your point. A major benefit of modern C++ is that it is much safer than the language Linux and PostgreSQL are written in.


Chromium _is_ written in C++, has some of the best coders and one of the best-tested codebases in the world. They're still finding zero-days based on exploits due to the language's failings.

There's a reason Mozilla has decided that all parser code should be moved to Rust ASAP.


> High quality teams like the Linux kernel team

Which very notably does NOT use C++.


Nor does Postgres.


I'd venture that the development slowdown from using rust is closer to 50% than 5%. Even just compile times probably slow down that much, not to mention you have to write (sometimes) as much as 10x code to express yourself.

The specific exercise I have in mind is a lockless thread queue. < 20 lines in C .. ~200 lines in Rust.


My anecdotal experience is the opposite.

I write code much faster in Rust than in C++. Part of it is thanks to the type system – fewer opportunities for dev errors means that I can produce code that is more concise, spends less time handling runtime errors because I can have static guarantees that they have already been handled somewhere, etc. Part of it is #[derive(...)], great documentation, Cargo and other QoL components of Rust.


Fair enough. I can imagine if you largely write code that plays nice with the compiler it could be faster. That just hasn't been my experience.


> The specific exercise I have in mind is a lockless thread queue. < 20 lines in C .. ~200 lines in Rust.

Why is this your exercise you have in mind though? This is such a bad argument. Like, yes, if you work at doublylinkedlist.com where your entire job is writing a new linked list implementation every day of the week, Rust might be a bad choice. But that's not what any kind of commercial enterprise actually looks like. If I saw you writing a lockless thread queue at work, I'd tell you to stop wasting time.


>> Why is this your exercise you have in mind though?

That was an example I could point to where the code size difference between Rust and C or C++ is roughly 10x. I mentioned it to corroborate the claim that sometimes writing Rust is very verbose, on the order of ten times more.

My point was not that average commercial codebases are largely dominated my these types of structures. My point was that my experience with Rust has been that the development slowdown is much more than 5%, as the OP suggested.

>> If I saw you writing a lockless thread queue at work, I'd tell you to stop wasting time.

And that's one reason we don't work together ;)


A lockless thread queue (not entirely sure what that would be, sorry; a queue in front of a pool?) seems like the kind of low-level library that could be written with liberal use of unsafe. No need to flagellate yourself on the altar of compile-time safety.


Then why would I use rust in the first place?


So that you can write "primitives" such as a queue in unsafe, and assuming it is correct, it is not possible to introduce bugs from other code.

Also, things like queues tend to be implemented in either the stdlib or a very popular library. So they are very well tested and likely to be widely reviewed.


I'm sure there is a ton of variance. I have done some Rust projects where I run into absolutely no safety complaints from Rust because it just isn't the kind of code that does anything the borrow checker cares about. For those development time ends up typically faster than C/C++ due to various syntax and tooling niceties.

Other projects will really get into domains where you have to work hard to satisfy the borrow checker and it can slow you down a lot. In a real application you won't be writing lockless thread queues for a big % of the time. But then for a real application the compile times will start to weigh on you more. (Though, C++ does not always compile fast either unless some care is taken to be sure it does)


And you're running at least one static analysis tool and a linter on your C++ code too, right? You're not just deploying the C++ compiler output as-is, right?

Those take time to run too and should absolutely be added to the compile time metrics when comparing against "cargo build".


On a personal project I run 2 metaprogramming passes, compile the entire project (~2m LoC) _twice_ and run the entire test suite in < 30s on a laptop from 2010.

I don't run a linter because I hate them, and my metaprogramming passes do a bit of extra static checking clang doesn't. I occationally run extra static tools (valgrind et. al) but they're painfully slow and very rarely catch anything.


Rust in incremental build mode is not slow at all, I really don’t see why people say so.


>The specific exercise I have in mind is a lockless thread queue. < 20 lines in C .. ~200 lines in Rust.

Do they have an equivalent API? Rust does have a hard time explaining very, very low level stuff that you'd have to do unsafe and various magicks. But once you get the unsafe details right, the consumer API for them tends to be extremely rigid and fool proof.


> Rust does have a hard time explaining very, very low level stuff

A little odd for a supposed system langauge


But it is just not true. Rust has a hard time writing algorithms where objects don’t have a singular owner. That’s all, it is not low level stuff. It for example has proper SIMD support, while the supposedly low level c doesn’t.


This is an ... interesting argument. "I can't write this 200LOC component of a large codebase in safe Rust, so what's the point in Rust?"


> High quality teams like the Linux kernel team and PostgreSQL, do periodically have serious security bugs that are things Rust would have caught.

Safe rust would have caught. If you had to drop into unsafe to do what they did, the serious security bugs would still have happened.


A fair point, though the few security issues in recent years I've looked at were not the kind of thing you would turn to unsafe rust for. But I've certainly not done a broad enough sampling to say what % of cases are like that.


To be fair, I am sure Rust catches a fair amount (compared to C++ which would catch 0). I just think that phrasing rust has having 0 memory errors can be a tad inaccurate.


Except when something goes wrong, you can typically focus all your attention on the unsafe blocks, which should be a very small portion of your codebase, if it exists at all. (Contrary to popular belief, unsafe Rust code is neither mandatory nor widespread.)

By contrast and comparing apples to apples, your entire C or C++ codebase is the equivalent of one giant unsafe block. You can bring in 3rd party tools to perform some of the static analysis the Rust compiler does for you, but call it for what it is.

By default, Rust is safe while C & C++ are not.


> There are however valid questions, like if Rust slows down your development say, 5%, would you get more net safety from spending 5% more time testing/fuzzing c++ code instead? etc.

I think a similarly valid question is how often you resorted to dynamic allocations just to get the borrow checker off your back. If your Rust version uses 5% more dynamic memory (with the corresponding performance and memory footprint penalty), is it perhaps worth staying with C/C++ and spending more development time on testing/fuzzing?


Some people make the opposite claim: Rust lets you get away with less dynamic allocation (and more data shared between threads) because you can rely on the compiler checking your work.

The effect might be positive or negative depending on the circumstances.


If you're willing to jump back to C++ to get around the borrow checker strictness, couldn't you just use Rust's unsafe block where you're sure it won't result in a bug?

Is it harder to fuzz Rust? Honest question, because fuzzing is something I occasionally read about but am not practiced in.


I've found it very easy to get started with fuzzing in Rust using cargo-fuzz. I didn't do anything very advanced, and my closest point of reference is testing Python with Hypothesis, but it did turn up bugs.

Here's a Rust fuzzing story from yesterday: https://hacks.mozilla.org/2022/06/fuzzing-rust-minidump-for-...

It claims that Rust is particularly suitable for it because integer overflow panics in debug builds (and out of bounds indexing always panics), which sounds reasonable.


> And what did we screw up? Some legit stuff! It’s Rust code, so I am fairly confident none of the issues were security concerns, but they were definitely quality of implementation issues, and could have been used to at very least denial-of-service the minidump processor.

This is so much better than the outcome would have been in any C or C++ project despite the many protestations of "just follow modern best practices" adherents. The author of minidump is no novice, is well versed in best practices in multiple languages including C++, was sure the code was solid, and still got spanked hard by the fuzzer. Denial of service outcomes aren't ideal, but they were likely fewer in number and are unambiguously better than security vulnerabilities.


That is a fair thing to wonder about, but at the moment most of the design decisions Rust pushes you toward tend to be better for performance on modern hardware. For instance using an array based arena to store a graph, instead of the traditional allocation of a chunk of memory per node on the heap. Or just keeping more stuff on the stack.


But it takes a lookup hit and you need an arena per object type with its memory overheard. You'll get more cache misses.

A lot of the rust performance talking points just aren't true. Rust is slower than C and C++, not by much but you can't get a true believe to even recognized this. Rust has turned into a religion.

Rust also disallows some things that you can do fine on C++ if you know our architecture because thing won't work on some machine 20 years ago (eg, it has more strict alignment requirements than any machine a consumer can see).

Throughput may only be 5% or more slower, but latency issues for rust is a much bigger issue. The devs I've talked to don't even try to pretend they have a good latency profile.


The linux kernel is absolutely anything other than high quality lol


I've worked in a variety of languages, and returning to a c++ project recently I do see that we spend a lot more time thinking about how to write the code in a way that avoids problems. Meaning that there's a lot more architecturing required to reach a sane state.

We have a sister product written in a dynamic language, and sometimes we have identical functionality.

I've noticed that when a change is discussed, the c++ gang has architectured themselves into the current solution and therefore have a much harder time making changes.

So for that reason I think it's easy to overlook these complexities when you're working in c++ alone; they feel natural and are just part of how you work. You forget that a lot of this architecturing just isn't necessary in a lot of other languages.


Maybe I'm wrong, but I believe that most of the architecting that you describe would be effectively what you do with regards to performance as well: Minimizing change of ownership, moving to a system with more static allocations with fewer "objects" that are linked into a variety of subsystems.


Yes, that's true. In a sense, c++ requires good code structuring.

That's also part of why I enjoy returning to c++, the people involved know how to structure code and create clean architecture.

That said, sometimes c++ does get in the way. Creating trees or graphs can be cumbersome, and IMO it's very biased towards virtual methods to solve polymorphism.

Extending lifetimes by pooling or similar is also quite common, and is in my eyes sometimes overdoing it. If you for instance use Rust, you can be a lot more confident that the compiler catches these issues, and be more conservative and efficient in the solution.


> very biased towards virtual methods

This is a function of the type of software you write. There are many large C++ code bases that rely on various types of static polymorphism almost exclusively, rarely having a use case for virtual methods or dynamic polymorphism. There is a similar story with inheritance versus composition; some types of code bases naturally gravitate toward one or the other.

The nice thing about C++ is that it as amenable to any of these models should it benefit the application.


I don't agree. The support for static polymorphism is in it's infancy at best.


Can you give some examples of what you mean? I use static polymorphism in C++ routinely and haven't felt particularly limited by it.


Sure. I guess template-based polymorphism is alright for code dispatch. But if you want to store the different types of objects involved you have limited choices. There's no straight-forward support for sum types. std::variant is fairly new, has a cumbersome API, and is quite slow (ballpark the same as virtual calls). There's no support for methods on enums nor anything for customizing the fields of different enum constants. There's also no pattern matching or other really convenient way of deconstructing variants.

So while it's there, I would say that the oo virtual method style is much better supported, although storage for those usually requires some type heap allocation.


Okay, yeah, I would broadly agree with this. I think most people use template-based polymorphism, which is pretty flexible in practice. The use of virtual methods is verboten for many common use cases of C++, due to the necessity of being in paged memory, so constructions for dealing with polymorphism without virtual methods are commonplace. And std::variant is a bit of a hot mess.


> The use of virtual methods is verboten for many common use cases of C++, due to the necessity of being in paged memory

I wasn't aware of this. Maybe I'm just out of the loop. Do you know where one can I learn more about this? I'm desperately trying to reduce the number of virtual calls in our codebase, but I'm hitting the aforementioned problems.


It is a design problem endemic to database engines and probably file systems. A design requirement for most of the dynamic runtime data structures is that they can be directly paged to storage, either in whole or in part, and be paged from storage in an arbitrarily distant future on different machines with different compilers. In order to make this work, all data types used in pageable data structures must 1) have a size and alignment that is not compiler-dependent so that page types always have a size that is a strict multiple of the I/O page size and 2) not contain any pointers. This precludes vtables.

This has traditionally been managed with CRTP, tagged unions, etc with some scaffolding to make it convenient and compliant with strict aliasing rules. Ideally, almost all of the dynamic polymorphism is pulled up to the level of the page types, an opaque blob of I/O friendly complex data structure, minimizing the amount you have to do. It is also important to note that JIT-ing has replaced many of the use cases for dynamic dispatch e.g. adding user-defined schemas at runtime.

None of which may apply to your use case. Some things inherently require an unfortunate amount of dynamic dispatch.


Virtual methods are about dispatch tables (which are built at compile time) and have nothing to do with paging. Nothing. At all.


"hmm, you call it an 'enum class' yet somehow I cannot define methods on it"


> and IMO it's very biased towards virtual methods to solve polymorphism.

Is it? These days I'd expect C++ to be very biased towards using templates for polymorphism. After all, templates are a thing that C++ provides with functionality that other languages often lack, whereas in the field of virtual methods/"dynamic dispatch OOP" C++ severely lags behind other languages. Choosing between two features of a language and using exactly the one that is worse in comparison to your competition feels wrong to me.


I do not think C++ "lags severely in the field of virtual methods". I use both OOP and template based polymorphism depending on particular needs and see no significant problems in either.


C++ can't do things today that Smalltalk and CLOS were able to do in the 1980s already; how is it not lagging behind in OOP? Hell, companies like Trolltech had to extend C++ for their own purposes to provide just a subset of the extra features (relative to standard C++) that had already been available in environments like Smalltalk or CLOS.


> Creating trees or graphs can be cumbersome

Wouldn't Rust have the same problem with this (if not worse)?


I was told that deferred_ptr/deferred_heap was supposed to solve these things for C++, so perhaps that would make Rust the option with worse problems in this department. Not sure where it got by now, though.


Sometimes (definitely not always) you can use std::function instead for dynamic polymorphism.


…and when you do need a large number of dynamically allocced/deallocced objects, then using indices to arrays instead of pointers. Which kinda defeats the purpose of using the Rust borrow checker…


I think this mirrors my day to day experience with C++.

On the other hand, fuzzing large c++ programs will routinely uncover memory safety issues in practically any large codebase that hasn't been absolutely beaten to death by fuzzers already.

The issues are not usually so much "I returned this thing on the stack" they tend to be things like "this (very unexpected) sequence of api calls will result in a UAF in this deeply nested data structure over here on the heap".


> The issues that Rust is supposed to help with are simply not what we spent time on. All the bugs reported are pretty much exclusively root caused to "business logic".

Everyone claims this, probably because business logic bugs are more memorable. But I've never seen it match the real statistics. According to the best published data, null alone is something like 30-70% of bugs, you just don't remember them because they're uninteresting.


It depends on the person's mental categorisation of bugs too. For example I could see someone classifying null bugs as business logic bugs because "the business logic didn't account for that information not always being there"


It's a language bug, really. If you're expected to say what the individual things in your program are allowed to be (like you have to in C++, Java, C# etc.), and you say "foo is a BarBaz object", and the language and compiler allow you to set foo to something that isn't actually a BarBaz object and this is considered OK, then the language is botched.


There are and never will be (meaningful) statistics for the "N percent of bugs are caused by X" question.

Every org's use cases are different, how do you get (let alone compare) data frm different orgs, who really counts their bugs anyway (and those that do at scale and in detail are probably doing suffering from some form of myopic management disorder or another), etc.

All you can do is ask people their gut take based on their particular experience. For systems engineers, a lot of bugs are due to memory safety. For more consumer-oriented startups (or in most any bigcorp), yeah, it's "business logic" (or people's inability / unwillingness to communicate), etc.

"We found that 70 percent of our bugs could have been prevented by moving to TypeScript", yeah sure.


I wonder if passing NaN is considered "business logic" Thinking of this interesting exchange long ago by an experienced c++ dev

https://old.reddit.com/r/rust/comments/78bowa/hey_this_is_ky...


Counterpoint: When I was on the Windows accessibility team at Microsoft, one of my most brilliant colleagues gave a presentation about what to look for when doing code reviews, and he emphasized three main categories: C++, COM (Microsoft's Component Object Model), and concurrency. Rust eliminates many of the issues in the first and third categories. And yes, we were writing modern C++ as much as we could in that legacy codebase; by 2019 we were using several C++17 features, as well as the latest C++ utility libraries for working with COM and WinRT. Given the state of the Rust windows crate, that team (which both I and that colleague left in late 2020) might even be able to use Rust in new code. I'm sure that would make him happy.


My manager told me exactly the same thing as you do; after I found a few UB in its code in the first 2 weeks he changed its stance.


I had to deal with a senior dev. that gave me a talk about seniority after I ran valgrind over our software. Guy was so deep into the whole senior dev. power trip that he blamed third party libraries for his bugs, dev. tools for "incorrectly" identifying his bugs and wrote more bugs to work around his other bugs.

Finding and fixing issues in C++ code can be easy with the available tools, getting people to use them on the other hand can can be like talking to a wall.


That sounds more like an expert beginner than senior.


Sounds like someone who senses his place in the hierarchy is threatened.


Unsurprisingly the types of bugs reported are going to be around business logic errors and not obscure edge case that users won't run into naturally. The bugs are still there though.


Right, and if you come across it you can't always reproduce it. "Oh it crashed, yeah it does that sometimes".


>The issues that Rust is supposed to help with are simply not what we spent time on.

There could be plenty of bugs in your codebase but just because you don't spend time on them doesn't mean they don't exist. Hundreds of millions of people used OpenSSL everyday for 2 years after the Heartbleed bug was introduced. It didn't cause any obviously broken code until someone exploited it to read credit card numbers off of a remote server.


Can you elaborate on the proficiency of your dev team, is this with juniors etc? Is it a large team? And what is the complexity of the project? I think this is important information


GPU driver, most devs are senior. Hundreds of thousands of lines of code in the "slice" my team is interested in. Team for our component has on it's own has probably over 40 people.

Driver should be even more prone to programming bugs because most of it is about manipulating data in raw "untyped" memory.


GPU drivers are also some of the most buggiest stuff out there I used. When I worked in Games we routinely managed to make the GPU drivers crash which thankfully at the time was already no longer taking down your engine machine.


My experience with GPU drivers then is that your code is likely buggy AF and your team is in denial.


In practice for you. Where I am, things are very different! Buffer overflows and memory corruptions, threading issues, uninitialized variables, etc appear on a very regular basis, and end up being very difficult to debug, mostly because the moment where you corrupt memory and the moment it triggers a bug can be very far apart.


Static analyzers can catch this stuff nowaways (in this case clang's builtin analyzer):

https://godbolt.org/z/1MfErrYd8

...in more simple cases it's also a regular warning:

https://godbolt.org/z/5v9YsWd4j


> less time debugging

I would say it isn't even the debugging. As many of the C++ programmers here have said as you get good at C++ this becomes something that you are vigilant for and it rarely actually gets written. But just the lack of even needing to think about it is a huge load off my mind. When I used to write C++ I never really realized how vigilant I was. Every time I added code into the middle of a function I had to double check all of the lifetimes, every time I shorted the lifetime of a variable I had to search for it to the end of the function. Just not having to worry about this much really frees your mind for thinking about other things.


And what are you thinking about now?

For me it was what lifetimes and types to use to make my program work. Just as it was in C++, but with a static verification step at the end which most of the time got in my way.

Rust makes lots of sense in high-churn projects or projects which have very high security requirements (like browsers). Otherwise I’d think carefully about using it.


You had to think about lifetimes in C++, too, but in Rust at least they're written down and enforced.


I find neither C++ nor Rust to be particularly “creative” programming languages because one has to think all the time about two things which are irrelevant for the features of the program - resource management and complicated types.

Rust is even worse than C++ because it highly encourages encoding logic in types and the community loves doing that.

You seem to have almost come to the same conclusion yourself, but then mistakenly assume that the same kind of productivity of a GC language is available in a system language. Nope. Although at least in C++ one can just say “fuck it” during the “creative” prototyping phase, copy most things and still have decent syntax and performance. In Rust you’d have to pepper everything with clone, boxes and (a)rc, so you'd have another mess.


The exhausting part for me was always when the bug escapes detection for a little while, is associated with some new functionality that very much did not escape detection, and it turns out that the wrong implementation is faster than possible. Now to fix the bug I get to be the bad guy and take away the customers toys (one of many many reasons to push for making the culprit fix the problem, especially if they don't want to).

It doesn't take many of these to form a coping mechanism that prevents this from happening again, even if it's at great cost. This is also the genesis of many unwinnable arguments that drag on forever.


Nope. In my experience, Rust saves you from very common obvious errors that junior programmers make, certainly not 10x programmers who have been writing C for a long time. It's great when you're starting out (which is why it's massively popular with new graduates or people who are just learning to program), but at some point it's questionable if the hand-holding Rust gives you is worth the extra development time overhead, more complex syntax, etc.

It also depends on what you're writing. If you're writing cryptographic routines, or protocol handshaking, the tradeoffs are heavily weighted in Rust's favor.


For the unironic use of "10x programmer", I'm hereby sentencing you to death.


Yep, all those kernel, driver, and database devs out there are all junior devs just starting out. No way anyone with decades of experience will introduce a remote exploit or crashing bug in their C programs and libraries. Never happens. No CVEs to see here! Move along!

/s


Why do you think this is a valid argument? Are you saying there are no junior devs working on databases, kernels, drivers? What evidence do you propose to back up that astounding claim?


> All it had to say was that rust tells you when you keep reference to on stack variable after it goes out of scope.

That's the root cause, but it's not the interesting bit.

The code in the article comes from a production compiler. And normally, the AST (abstract syntax tree) is a single data structure output by the parser. Ownership is simple: the entire AST has the same lifetime, and it's managed by a caller. This should be easy, right?

But it turned out that there was a piece of code that sometimes "synthesized" extra, temporary AST nodes. And these nodes had a shorter lifetime than the rest of the AST.

These are vicious bugs. You have some long-standing convention about how things work, but one little piece of code makes an exception (often for excellent reasons). Then another module decides to make an aggressive optimization that relies on the original assumption. But that assumption is now true only 99% of the time.

It's a communication failure, and it might take years to actually turn into a bug. And that bug may manifest as extremely rare memory corruption that shows up in automated crash reports.

Running down this kind of phantom memory corruption is one of the most frustrating things I've ever done. It often involved spending weeks staring at minidumps, looking for interesting patterns in crashes. There's that horrible moment when you realize that 20% of your crashes occur within a thousand instructions after a particular font-rendering function reports an error, accidentally corrupting the exception-unwinding machinery.

And sure, I get it. Maybe your team is simply good enough that nobody ever makes a mistake like this. But if so, they're exceptional. I've worked on amazing teams that still get bitten by subtle miscommunications and misunderstandings.


Reviewing commits for a security-critical project written in C or C++ can be incredibly tedious. I've spent an entire day trying to validate that the assumptions made by a 10-line change are memory-safe in the context of the larger program. These reviews are incredibly mentally draining, and even when I'm done I'm not 100% sure that I didn't miss something and let a vulnerability into the codebase.

Rust is a breath of fresh air in comparison. Worrying about memory safety isn't even a concern for the vast majority of commits that don't touch modules with unsafe code. All assumptions made about the lifetimes of references are made explicit in the code, and checked by the compiler. On rust projects I find I have much more mental energy to use against other aspects of the problem.


I agree with you on the first point, but I totally disagree with you on the second point: sure if you know how to reliably reproduce an issue it isn't complicated to debug, but this kind of issue can be difficult to reproduce, can create silent issues..


> You get corrupted data, set data breakpoint and in the provided example you will see it being modified by unrelated operations on stack.

That's provided that you can even reproduce the issue well, especially in an instrumented build which might be way slower than the non instrumented one. Often you get bug reports like "crash after one hour of usage" where basically every feature of the app has been heavily used by multiple users. Rust applications might still crash but they crash safely, which means your error messages are more meaningful.


It all depends on the complexity of your application. The type of guarantees Rust offers in my experience becomes exponentially more useful as your application grows in size.


This indeed. Small local console app running on trusted data? Maybe an hour to track down some memory corruption if you're particularly unlucky, in which case shuger's kind of got a point: who cares?

Large network-exposed app? Individual memory corruption heisenbugs have taken me weeks to track down (and weeks before that for QA to create a reliable repro for) - a needle in a huge haystack. They often predate my employment - having lurked semi-silently for who knows how long causing who knows how many unreported crashes. When release dates slip because of bug backlogs filled with memory safety related crash bugs, when ~70% of many vendor CVE reports are down to memory safety issues [1][2][3], and when you personally have to deal with the fallout of all that: shuger's point completely and utterly evaporates.

[1] https://msrc-blog.microsoft.com/2019/07/18/we-need-a-safer-s...

[2] https://www.chromium.org/Home/chromium-security/memory-safet...

[3] https://langui.sh/2019/07/23/apple-memory-safety/


Just because Rust, in particular, compiles successfully, that's no guarantee that the code isn't complex and difficult to understand. I can write a complex badly designed app in any language. Similarly I can write simple well designed large applications in any language too.


Yeah, but that doesn’t mean all languages are created equal in that department. Let’s take this pseudocode:

    int a = 0;
    if (findindex(mylist, myvalue, &a)) { 
        // dostuff with a
    }
Here, findindex returns false if it can’t find the value. The problem is that there’s nothing forcing you to use the if, you can just forget it and you’ll be left with incorrect code. In Rust, this type of error is impossible to make by accident, because the findindex function would return an Option, and you have to explicitly handle both cases (or explicitly say you don’t care about one of the cases).

Things like that, along with the lifetime system, make it easier to write good code. It’s like saying that it’s possible to destroy your foot with a shotgun and with a pencil – it’s possible, but it’s a lot easier do to by accident with the shotgun.


This fallacy gets repeated over and over again and it doesn't make it any less false.

Languages are tools and some tools are actually better-built than others. If we can claim that a language like Brainfuck makes writing clear code extremely difficult, and Python or Rust make writing clear code easier, we've already established that there's a spectrum for language in expressiveness and clarity.

Eliminating entire classes of bugs makes for better understanding.


> It all depends on the complexity of your application. The type of guarantees Rust offers in my experience becomes exponentially more useful as your application grows in size.

Sure, but the development pattern that get used for larger applications typically don't benefit from Rust's additional safety; for one you're going to be using a garbage collected language.


> for one you're going to be using a garbage collected language.

I wish it were true, but promise you it is not. As a counterpoint, I point to most "AAA" gamedev and OS development.

In gamedev it's even a bit flipped: The smaller indie gamedevs can pay the GC hit for Unity's C#, web JS, actionscript flash back in the day, etc. - not much working data, not much garbage. Larger scale titles start missing vsync and having horrible stuttering when GCs are thrown into the mix too brazenly - they're still used on smaller scales (embedding browser tech for UI, limited scope scripting, etc.) but they have a lot of native, non-GCed code.


Agree; this is not an issue that slows down my development or bughunts.

You can get a long way towards safety without learning Rust. It's those rare cases that will get you.

It's a trade-off; take the time to learn the language and deliver later, or just use what you already have to deliver a product now.[1]

[1] During a Rust discussion some years back, when I was at a different company, on a specialised and large-ish product written in C++03.

I went through about 3 years of tickets (limited to only the bugs reported). No open ticket was older than a few weeks. Out of maybe 1000 bugs, only a single one was something Rust would have prevented. I would think that most mature products will have similar stats, so the trade-off is not as obvious as it looks to be on the surface.

Deliverables matter.


It probably depends on project type and how complex your ownership models are, but that doesn't really track with large projects having a majority of their CVEs be memory safety issues that are far less likely in Rust[1] (e.g., https://www.chromium.org/Home/chromium-security/memory-safet...)

[1] I say far less likely because obviously it's possible with unsafe Rust, but I've never had one happen, seen one happen in real code, or been affected by part of a dependency tree having one.


I'm not saying that a large number of CVEs won't be prevented in Rust, I'm saying that so few bugs are CVEs that the trade-off is not always worth it.

If you have 1000s of bug reports, of which 5 are CVEs, and then have 3 of those 5 be preventable, most dev teams are still going to consider the cost/benefit of going through the pain of developing a long-term product in Rust, or of switching to Rust altogether.


> of which 5 are CVEs

Those 5 are just the ones you know about...


> Those 5 are just the ones you know about...

It's pointless making a cost/benefit analysis on things that probably don't exist.


I suppose it comes down to risk assessment; if those CVEs are critical “fix this now or the world catches fire”, then their relative infrequency seems to be outweighed by their impact, no?


> I suppose it comes down to risk assessment; if those CVEs are critical “fix this now or the world catches fire”, then their relative infrequency seems to be outweighed by their impact, no?

No.


CVEs are a tiny, tiny fraction of bugs.


>I went through about 3 years of tickets (limited to only the bugs reported)

This statement is meaningless without any insight on how bugs were created. If the bug reports exclusively dealt with "happy-path" or "business logic QA", then of course you won't see any CVEs. Did the use of fuzzers or address sanitizers create bug reports? Were these tools even used? If not, the claim that only one of 1000 bugs were memory safety issues isn't credible; you weren't looking for them so of course you didn't find them.

I think it says a lot when almost every C++ developer claims to have a higher quality code base compared to say Linux or Chromium when it comes to memory safety errors.


What is it about C++ as a community where most average devs claim an inhuman level of proficiency but only the coders with experience in real critical codebases have the humility to claim that without extremely strict coding practices and extensive use of fuzzers we're barely smarter than apes at churning out safe code?


> All it had to say was that rust tells you when you keep reference to on stack variable after it goes out of scope.

The author isn't just telling you that Rust is awesome because it tells you something, he's acknowledging the frustration in learning how to listen to the compiler.

It's kind of like Jerry Pournelle describing the ups and downs of USB by documenting an epic journey that all started with trying to scan some handwritten notes for his next novel using a Canon scanner he borrowed from Alex that he's just now getting around to reviewing, because the pins of the parallel port are too bent to use the old Epson. Okay, so maybe it was a little overcomplicated.


Earlier this month we integrated a C++ library written by my team with a server written by another team.

We saw the data corruption, and we knew it was a reference issue, but it took quite a bit of effort to track down. The cause was confusion around string_view and string&, with different behavior when you pass each to a new thread.

Rust would have caught this much earlier and saved 3 days work.


This case might be trivial to debug, but when you start adding concurrency a lot of that ease goes out the window. Right now, our tests are mildly flaky because of asan crashes from use after stack frame issues. Reading the code it should be joining all the coroutines on destruction, but yet the asan violations are happening. It really isn't that trivial to debug. Luckily in our case it doesn't affect production since it's only on shutdown (probably...?).


As someone considering learning Rust, the article put me off fast with the long preamble to even explaining what the issue was. I really hope it's not that complicated, but even your rebuttal fills me with fear - "corrupted data [......] being modified by unrelated operations on stack". How would you explain that to put a C programmers mind at ease?


It's unfortunate how many upvotes this got from the title


Rust is undoubtedly a step forward in correctness, but boy, the article's code is so thick that for me, that I am a C++ programmer, is almost unreadable, especially for a quick reading...

I skipped all the code and only read the text. I got to the point where it mentions that the language prevented them from storing a pointer to a stack object in a heap object, and that's really great, but boy oh boy, the code presented, and the error messages, where very difficult for me to grok even after the first few times...

It seems to me that the more sophisticated the safety a language provides is the more complex its error messages/code are. It would be so nice if we could solve both problems at the same time...


I skimmed the code and there are a ton of language features in there that you might not be super familiar with:

- pattern matching is quite expressive and ubiquitous in Rust, as in "stuff on the left" is almost always a pattern even in places that you might not expect at first

- lifetime annotations, a form of generics, are quite unique and make the code more verbose/noisy if you are not familiar with them

- Rust is expression based (a bit lispy) so you might not quite see the flow of the program as well if you are used to statement based syntax

- traits are are an important part of the language and some of the methods and method chains you see there might look arbitrary, but many of them are very common. A more experienced reader can identify many of them and see the code very differently.

- there is no syntax highlighting on the article's snippets so you already have to know how to parse them


As a Rust programmer I think the code could have been made much simpler and still get the point across.

I think the author wanted to replicate the code as closely as possible, so it's more a case study than an educational piece if that makes sense?


For whatever it’s worth: I think it’s harder to learn rust as a C++ programmer than it is for a new programmer.

Reason being that you have to unlearn your paradigm before you’re able to learn the new paradigm; and it’s made slightly worse by giving the appearance of similarity (since certain concepts map directly, like flow control and loops).


One big hurdle for C++ programmers learning Rust that I've seen time and time again in various community support channels is fixing their trust issues with the compiler. They are so used to compiler output being useless or worse than useless, they are unable to take advantage of rustc's stellar diagnostics (which frequently tell you exactly what you need to change to make things work) until they force themselves to actually read and internalise them.

Once they realize that the tooling is actually working _with_ them, not _against_ them, they start to pick things up much quicker.


> big hurdle

Without going much further, to me it's the syntax. I can write some Rust lines of code, and I appreciate the compiler output to correct my syntax. But it's really difficult to me to read code written by others. I can't open a random file of a Rust project and have a minimal clue of what's going on

After years of C and some C++, I have a parser implanted in my cerebellum, and it's hard to adapt it to Rust. To me, reading Rust feels like forcing an optical nerve to see something very small.

Case in point, the code in this article.


Oh yeah, I feel your pain, my brain is really used to Rust/Swift and Python syntax, and when I have to read Typescript/React, C++ or something like Nix, it gets hard for my brain to parse it. Loved the eyesight analogy!


In my experience only beginners have this issue, as a C++ programmer the compiler is my best friend.


> It seems to me that the more sophisticated the safety a language provides is the more complex its error messages/code are. It would be so nice if we could solve both problems at the same time...

That is incredibly hard, yes. But at least if you are ok with the overhead of GC or RC then this is a solved problem - the bug in question here wouldn't happen in Go/Swift/C#/Java/JavaScript/Python/Ruby/etc. And that is most software today.

(But when you need maximal performance and maximal safety then Rust is an incredible option!)


After reading a good chunk of the Programming Rust book (minus concurrency, async and macros) I can understand that. My C++ experience helped a lot in comprehending and mentally mapping Rust concepts.

As far as I can tell, that’s normal Rust code, but yes it’s unreadable if you come from C++ or another language and don’t know Rust syntax. The funny thing is that this kind of code is supposed to be the readable part of Rust :-)


(Offtopic) Hey axilmar - I found your comment on a different thread[0] really interesting, and would love to chat with you about it. Couldn't find your contact details. Would you be open to emailing me? My email is on my profile. PS: Not a hiring / sales attempt.

[0]: https://news.ycombinator.com/item?id=22288631


Hello Axilmar. Incase you were unaware, a strong push for a DFUW relaunch is making strides, and team members are interested in having some discussion with you that include a possibility of being hired in some capacity. You can contact me on Discord as TonY#2011 and I can put you on direct contact with investors for more details. Hope to hear from you soon!


Look at Ada and its error-messages.


I have only a small dabbling in Rust and it was to create a Postfix milter. What was awesome was that someone had already written a Rust crate so that I didn't have to do anything too horrific. The compiler on Ubuntu just worked as expected but, of course, as something that is not standalone, Rusts strong type-checking did not save me from writing something that didn't work.

Part of the confusion is the very confusing (for a noob) difference between String and &str and as someone who has written C and C++, I don't have a problem with understanding references and addresses but I didn't find a very good explanation. This led me to writing code using the only way I could get it to compile that didn't crash at runtime (which was nice!) but it also didn't work.

The second issue was that being something plugging into an external daemon, at the interface level, we were getting a raw pointer and had to convert it into a string to use it. I tried so many combinations but couldn't get it to work but thanks to the maintainer of the Rust crate (who obviously knew what he was doing) he told me where my code was wrong and I fixed it.

So I guess good in that there were probably no horrific bugs, although this is much like C# or possibly Java. But yes, the strong checking doesn't stop things from not working.


Yes... but before you start feeling too sure about Rust protecting you from creating bugs, also consider this more cautionary (and very entertaining) tale posted on HN today: https://hacks.mozilla.org/2022/06/fuzzing-rust-minidump-for-...


You mean the tale of someone rewriting a very complex and fiddly software from scratch, having it work great in production, and then using the ecosystem's great tooling to easily find and eliminate even more bugs than what the language already protected them from?


The thing they rewrote also worked great in production.

And fuzzing originated in C / C++ tools and is available for them as well, probably more diverse and mature than what is available in Rust.

The point you did ignore was: Rust is being sold as magically making software safe and people selling that completely ignore the fact that logical bugs are also a thing.

Which Rust evangelists find difficult to acknowledge because security is that one thing that Rust has going for it.

So difficult that you would rather try to misdirect than to acknowledge that software written in Rust will crash. And when processing arbitrary user output, it'll crash badly when not written with care and fuzzed a lot. Kind of like C++.


> The thing they rewrote also worked great in production.

It didn't for them, that's why they rewrote it.

> Rust is being sold as magically making software safe and people selling that completely ignore the fact that logical bugs are also a thing.

By whom? I don't think memory safety bugs are the only class of bugs, and neither do you, so who are you arguing against?

> Which Rust evangelists find difficult to acknowledge because security is that one thing that Rust has going for it.

Hard disagree. Rust also offers a solid type system, pain-free dependency management and all-around good tooling, just to name a few. It's much more than a "memory-safe C++" or whatever.

> And when processing arbitrary user output, it'll crash badly

If your definition of "badly" is "at a well-defined point, without any memory corruption", sure. But even here memory safety prevents what could be a much more painful debugging experience.


> The thing they rewrote also worked great in production.

It may well have, but the Rust version “was faster, it crashed less, and we even knew it fixed some issues”

https://hacks.mozilla.org/2022/06/everything-is-broken-shipp...


> The point you did ignore was: Rust is being sold as magically making software safe and people selling that completely ignore the fact that logical bugs are also a thing.

There are straw men and then there are straw man armies. This is probably the biggest one I've seen yet.


Ohh, I got one. Rust is less secure than C++ because it causes people to be overconfident in its security properties. The sharp edges of C++ are a feature that cause people to focus on correctness, not just get it for free.


You jest, but that used to be an argument against making seatbelts mandatory in cars. I suppose people would drive even more safely if there was a sharp metal spike sticking out of the center of the steering wheel.


This assumes that each person is responsible for their own safety. But you have no control over the other guy that puts people at risk. By mocking it, maybe we can prevent this form of argumentation earlier?

Hopefully in 20 years or less we will see unsafe code in the same way we view leaded gas, cigarettes and CO2. The other thing I realized in the last couple years is that eventually, everything becomes safety critical to someone at some point. That if you think you have to just protect this one system but that others can fail, is flawed on large scales. You can't tractably realize the failure graph, and nor can you prevent a seemingly innocuous thing from being used in a critical way.

The pandemic was a great lens into how human resilience kept flawed systems functioning in a way that few realized were flawed. The pharmacies in my area are still recovering only to have their problem exacerbated by more rigid controls that prevent local autonomy from making the system resilient. In this particular case, automation has made a problem worse by preventing humans from doing the little local silent repairs that they were previously doing.


The great (unintentional?) irony of that argument is that before seatbelts, that was arguably how cars used to be manufactured. Back then, drivers were often killed in car crashes by being impaled on the steering wheel. Those in the passenger seat would die from being hurled through the window. Despite the elevated risk in those older cars, drivers weren't safer --- they were unsafe to such a degree that it formed the argument for mandating seat belts in the first place.


Hell, this is a current argument some people have against wearing bicycle and motorcycle helmets.


Is it? For bicycle helmets I just know these variants:

1. Wearing a bicycle helmet is unpleasant, so people will cycle less and possibly use other modes of transportation such as cars. The risk of a traumatic head injury may decrease but this is offset either partially or fully by other health risks such as obesity.

2. Other road users in heavier vehicles (e.g. cars) perceive riders wearing bicycle helmets as less vulnerable, and so keep less distance when overtaking them, thus wearing a helmet increases the risk of collision.

I don't think I've ever heard someone say they won't wear a bicycle helmet because it would make them personally drive more dangerously.

I personally strongly oppose mandatory bike helmets. I think one of the reasons so many people own and ride bikes in my country (The Netherlands) is that we don't have mandatory helmets. Making helmets mandatory for people inside cars would also prevent or reduce head injuries (and indeed that is why they are worn by race car drivers) so if we ever introduce mandatory bicycle helmets here perhaps we can introduce mandatory car helmets at the same time, to ensure that people won't just take a car instead.


> The point you did ignore was: Rust is being sold as magically making software safe and people selling that completely ignore the fact that logical bugs are also a thing.

You know, I'm a Rust evangelist. And I can tell you, why I ignore logic bugs when trying to sell Rust. Rust can prevent some logic bugs if they break invariant you managed to encode or enforce in your types. But it is a difficult topic to dive in a internet discussion. The best I could do is to bring some anecdotal data points, like &str being guaranteed to contain only valid utf8 strings. The worse issue (for a discussion) that it may be not obvious what kind of a logic error can be fixed by the type invariant discussed. So to show it I need to find some more examples of bugs that were prevented by enforcing the invariant. But you see, bugs that were prevented were not documented. It goes like this: you write code, it doesn't compile because rustc is unhappy, you fix code and it compiles nice now, you call it a day and move along. When you fixed this small issue it was not a bug, just one of a several complaints of rustc. You would need to speculate a lot about what could happen bad if rust allowed this issue to live.

We can try to get to the point from a different angle and to find logic errors in a wild, and then to speculate how type invariants might prevented them if programmers had written their types in a some particular way. But it raise a question: would they write their types in this way before they faced this particular logic error?

If I tried to talk about complex relationships of Rust and logic errors, I would need to dive into an ocean of speculations. Rust deniers are not very cooperative on this regard, and I'd bet they would just dismiss all the speculations as... well... speculations. And they would keep insist that if we cannot measure a falling frequency of logic bugs, then it is all immaterial. Some even go further and claim that if some tool doesn't prevent all bugs of a kind, then it is a useless thing.

So it is not a fun to talk about logic errors with rust deniers. Among rust evangelists the talk doesn't happen either, because they do not split bugs into two categories "logic" and "memory" bugs, they split them into "can be prevented by type invariant" and "cannot be prevented by type invariant, or too f*king difficult to". They talk about memory safety to laymen because it is something that laymen understand and it doesn't need explaining. And Rust can prevent all the bugs of this kind, so even when we talk to a people who thinks in black-and-white, we can make statements that are defensible in this Aristotelian tradition of excluded middle.


You often encounter this entire thread of rhetoric when someone wants to put a diversion into the central argument, yeah but it doesn't ____.

But Rust does do that, match exhaustiveness, forcing the handling of errors and the type system enables things like CreuSAT [1] using creusot [2]

[1] https://news.ycombinator.com/item?id=31780128

[2] https://github.com/xldenis/creusot

> Creusot works by translating Rust code to WhyML, the verification and specification language of Why3. Users can then leverage the full power of Why3 to (semi)-automatically discharge the verification conditions!

Units of Measure, https://github.com/iliekturtles/uom

The base properties of the language enable things that can never be done in C++.


You mean like Boost.Units?


Great example of the kind of cherry picking in these conversations. It is insincere and uncharitable, all you did was show your colors. Didn't say everything was exclusive to Rust, while you make no mention on the ground breaking work in CreuSAT. Given as an example of the kinds of things besides memory lifetime tracking that can make Rust code more robust against logical errors.


> Rust deniers

Wow.


> Rust is being sold as magically making software safe and people selling that completely ignore the fact that logical bugs are also a thing.

I don't think that's true. Sure, there are zealots who oversell things, but reasonable people (including those who are actual leaders in the Rust community) are realistic about the classes of bugs that Rust eliminates, as well as those Rust doesn't help with.


Rust is interesting in that if you really thought it was magical safety across all bugs, you're probably not smart enough to compile a real application, let alone actually have read enough documentation to do more than a hello world.


What you said, although I would have probably phrased it less harshly :)

But yes, my point was that there are a lot of enthusiastic articles about how Rust can prevent "whole classes of bugs", which mostly gloss over the fact that there are other classes of bugs that it doesn't prevent. Of course this doesn't mislead experienced developers, but I'm not so sure about novices or less technically inclined people.


It seems like it is true and accurate to say that Rust prevents “whole classes of bugs”. It is obvious that other bugs can still exist. Otherwise people would say Rust prevents “all bugs”.

Are you disappointed that Rust evangelists don’t spend time talking about the fact that bugs can exist in Rust programs? Did you see the two articles posted in the last week alone about bugs that were found in Rust code? They reached the front page of HN

- https://hacks.mozilla.org/2022/06/everything-is-broken-shipp...

- https://hacks.mozilla.org/2022/06/fuzzing-rust-minidump-for-...

If you want to see criticism of Rust, I’ve found that the most informed criticism comes from people who use Rust a lot. Example - https://matklad.github.io/2020/09/20/why-not-rust.html


which mostly gloss over the fact that there are other classes of bugs that it doesn't prevent

That's nothing more than a technically-dressed version of whataboutism, isn't it? Or can you show other languages that do prevent those other unnamed classes of bugs?


Er... no. I was simply saying that the many articles about how reliable Rust is and how many bugs it catches for you might lead to a false sense of safety. Nothing more, nothing less.


What's the use of standing on that anti-static mat and wearing that grounding strap? Can't you see there are fork lifts zipping around just right in the next room? That mat is giving you a false sense of security!


This seems like the opposite of a cautionary tale, if anything it makes Rust seem an even better choice.


Well you’d have these bugs, plus the memory bugs in other languages.


They already did. Hence the rewrite from C++ to Rust.


I have run "cargo fuzz" (based on the AFL fuzzer) over a binary-format parsing library I wrote, generating roughly a billion inputs. Here's what I learned:

1. Rust does not protect you at compile time against "index out of bounds" errors. If you want that, you'll want a dependently-typed language like Idris. Which still wouldn't help much in this case.

2. However, Rust catches index errors perfectly well at runtime, and performs a controlled panic. This does not result in a privilege escalation, but it may cause a denial of service. So fuzzing in (safe) Rust is normally a "lower stakes" activity.

3. "cargo fuzz" is super easy to use, and AFL is pretty amazing.

4. If you think you can parse complex, badly-documented low-level binary formats without ever having an index error, you're probably wildly overconfident. In the case of the minidump parser, it's worse: The data was generated by a crashing process, and in many cases, the data caused the crash.

Let's look at what the author concludes:

> And what did we screw up? Some legit stuff! It’s Rust code, so I am fairly confident none of the issues were security concerns, but they were definitely quality of implementation issues, and could have been used to at very least denial-of-service the minidump processor.

A whole bunch of issues, probably none of them leading to security escalations. And unlike many C fuzzing experiences, it was largely painless:

> By comparison I am absolutely thriving under “Yeah you can deterministically trip this assertion with this tiny input you can just check in as a unit test”.

This happens because Rust catches most of these errors very early, using assertions in the standard library. So your fuzz reports often need to be tracked for only a few lines.

TL;Dr: Parsing complex binary formats usually involves subtle bugs. Rust does not promise to catch index-out-of-bounds at compile-time, but it catches them at runtime. This makes fuzzing easy and productive.

Overall, I'd call this a success story. The author underwent an inevitably humbling experience for low stakes under controlled conditions. And now the library can parse corrupted examples of an incredibly nasty format, with high confidence that the worst thing that will happen is a runtime error (not even a DoS).


> I needed a bit of palette cleanser

This is a fun mistake. It’s “Palate cleanser”.


Maybe they just want to drink solvent. A common reaction after dealing with very bad code for a while.


Works with palette (as in painter's palette) too - start with a clean slate, use a whole different set of colors etc.


Rust's safety features have never been its main selling point for me. Modernity and (recently) momentum are. That it's impossible to author certain classes of bugs is a nice bonus.

That said, I never want to see another rounding bug in financial statements that happened because some piece of code implicitly converted a string to a float and it kinda almost worked every time. To quote a cliche, I'm too old for that shit.


(Probably float to string and back, actually. Some mess like that anyway)


I've used Rust and I like it alright but it has the ugliest syntax of any major language since Perl


Have you seen modern C++? It has had new syntax bolted on every couple years for the past few decades, the result is quite phenomenal.


Can you name what new syntax you abhor?

I mean, other than []<>(){}() being a valid C++ expression [0], C++ hasn't even made any notable sygil-related syntax changes recently. Sure, some keywords were added (auto, constexpr, co_yield...) but I don't see how that leads to "ugly" syntax.

[0]: That's a lambda capturing no state, having no template parameters, taking no arguments, with an empty body, finally being called immediately with no arguments. But half of that is optional. A more usual lambda would look like [foo](int bar) { return foo+bar; }.


Anything with templates, type traits, especially when nested. (The kind of spew you get on an compilation error). Even

  template< typename T >
  struct foo<T, std::void_t<decltype(++std::declval<T&>() )>>
       : std::true_type { };
looks terrible and that's just basic.


You will notice that this is almost valid Rust. (Rust is a very small bit better, not enough to matter.)

In fact, a lot of what the Rust syntax ugly was taken from C++, and it took most of what makes C++ ugly. So the entire discussion is just amusing.


I still wish Rust had a way to express decltype tho. `impl Trait` doesn't always work, and when it doesn't it's quite frustrating.


What limitation do you encounter with `impl Trait`? They might be removed by `type Alias = impl Trait;` soon.

https://rust-lang.github.io/rfcs/2515-type_alias_impl_trait....


I find the new user-defined literals syntax pretty ugly. Useful, but ugly.

On the older side: the initialization list syntax has always been ugly (and bug-prone, due to the unintuitive evaluation order.)


'requires requires' makes me cry


Technically auto was already a keyword (from C) and was just repurposed. Pedantic but interesting. https://en.cppreference.com/w/c/keyword


Modern C++ looks horrendously ugly if you are a library writer, but on business logic it looks a lot tamer than rust.


This is my trouble with Rust. Thankfully Ada will do the same things addressed in this article while having a much more readable syntax.


agreed, I’ve tried to pick up rust a few times and the syntax has always made me give up halfway through the point of writing a real project. It’s really a shame since they had every opportunity not to make the syntax horrible. CPP has horrendous syntax but a lot of that is because they needed to start representing features over time that didn’t exist from the ground up. Rust had pretty much all of its killer features form the start but for unknown reasons elected to use a tremendously noisy syntax that seems to be inspired by the monstrosity that is modern cpp syntax.


It shouldn't be surprising that Rust's syntax is inspired by C++, Rust is literally designed to entice C++ programmers, and the syntax reflects that (modulo a handful of irresistible ML-isms to match the type system).


A garbage collector has saved my brain from overfrying many times.


When I was first learning Rust, it was exactly this sort of lifetime issue that tripped me up and frustrated me, until I finally saw the light and understood how things work.

Yes, the article is overly technical and detailed, but it captures very well the eureka moment of understanding Rust’s memory management. Some aspects are subtle yet profound.

I am always happy to take a little more pain at code-writing and compile-time in exchange for more safety guarantees at run time; Rust is great in this regard.


I was looking for some kind of story related to cooking bacon with Rust -> disappointed. Bacon is only used once - in the heading...



Similarly expected a firmware write-up for an IoT BBQ.


"Each method is defined on this Context type that carries some common state, and the methods tend to call one another."

This is classically a problem of GUI systems, where you have a large number of interlinked on-screen objects constantly undergoing modification.


And hence the general recommendation of having a single direction in your input/render cycle:

- wait for input

- a dispatcher take any input and turn it into a queue of model update actions

- pop queue to perform latest model updates.

- trigger a full tree rendering, passing the new model

- start again

So:

- the model is a dumb static data structure

- the dispatcher is the responsible for model updates and is a single entry point for the data flow

- the UI and the model don't know about each other

That what ELM, react and so on do now. Monodirectional MVC. It's hard to optimize the rendering though, but it's way easier to reason about when concurrency gets high.


> - trigger a full tree rendering, passing the new model

I agree the Elm model makes it easy to reason about it.

Just because we pass "the state of the world" each time instead of mutating it, does not mean that we need a full tree rendering. That is an implementation detail.

We can update just the relevant part. If we think of it mostly as pure functions a lot of it is easier to memoize and — for same input — it is possible to return the same output straight away.


That's what I mean by hard to optimize. Memoization is hard to get right, because if you compare only by value, it can be slow, but if you compare by identity, the cache can take a lot of memory.

And of course, forcing a purely immutable model comes with its own unique challenges.


So many words to describe a simple issue of using reference to out of scope data. Anyways I am using C++ a lot and this types of bugs are easily caught by analyzers or straight up by modern IDEs like CLion (you get a warning).


If only his webserver were written in Rust.


If only rust caught blogging security errors.

It's a terrible idea to post a photo of your child with her name on your public blog.

It's one of those subtle security things that the rust compiler helps with; but not here, sadly.


From the article:

    It’s pointing out that we are pushing into this vector
    which needs references into “the AST”, but we haven’t
    declared in our signature that the ast::Ty must actually
    from “the AST”.
I think someone needs to write a typechecker for English to help out the author of this sentence.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: