Live data from Hacker News

Several core problems with Rust

bykozy.me

321–330 of 341 posts

Re: Several core problems with Rust

#321

Earlier quoted context omitted.

Rust makes it very, very easy to .unwrap() something, making panic-crashes the default error handling behavior for a lot of rust programmers. But it does this without, e.g., Erlang's reliability services that will auto-relaunch code that crashes. The end result is NOT a very good user experience. I say this as a Rust advocate and daily Rust programmer, btw.

When it comes to distributed services, unwrap is likely the behavior you do want, because the service is being run by a scheduler that detects failures, and a panic would be tied to a metric and page an sre. My read of the situation is that they were overwhelmed by their dashboards which made it harder to identify the root cause, but that is a common situation for these kind of events. I'm pretty sure that this exact…

Yeah. Its also weird that people online are fixating on unwrap - as if changing or fixing unwrap's semantics would have helped cloudflare here.

The best way to think about unwrap is that its like an assert. If an assert trips, the answer isn't to remove the assert and just hope for the best. Asserts are almost never the problem. The problem is whatever happened right before the assert. Ie, the buggy codepath that generated the erroneous state in the first place.

In cloudflare's case, the bug was that their code required that a database query returned less than 200 results. Only the database returned more results than that. This isn't a problem with unwrap. It was these two mutually incompatible pieces of behaviour colliding in code. I don't see why rust has anything to do with this at all. Sloppy programmers can make a mess in any language. Rust is no exception.

Re: Several core problems with Rust

#322

Earlier quoted context omitted.

> How can my unsafe block make sure an allocation stays alive past the unsafe block itself? Put it in another data structure or something? Listen, if you can't make the invariant work, then you need to change the function. An unfixable unsafe is not an excuse to allow errors > I'm saying this is something to be aware of on projects with multiple people who may not catch something like this. It's good to be aware but…

> It's good to be aware but put the blame in the right place. It's the unsafe code that's actually at fault. If you are seeing corruption, look at the unsafe code first with an adversarial mindset. Yeah, that's why my first comment in this thread was "Just having unsafe in your codebase means changing code outside the unsafe block could cause UB". You can caveat that with "that's bad code", but that's the reality. >…

As I said here[0], although I can't speak for what @Dylan16807 intends, invariants required by unsafe code are required exactly to the extent that some code can alter the invariants (the module boundary). In this sense, Rust's unsafe is just a particular example of encapsulation, where all notions of invariants in programming have the same essence.

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

Re: Several core problems with Rust

#323

Earlier quoted context omitted.

> It's good to be aware but put the blame in the right place. It's the unsafe code that's actually at fault. If you are seeing corruption, look at the unsafe code first with an adversarial mindset. Yeah, that's why my first comment in this thread was "Just having unsafe in your codebase means changing code outside the unsafe block could cause UB". You can caveat that with "that's bad code", but that's the reality. >…

> Yeah, that's why my first comment in this thread was "Just having unsafe in your codebase means changing code outside the unsafe block could cause UB". You can caveat that with "that's bad code", but that's the reality. I agreed with you that changing safe code could trigger the bug, but the safe code is not where the bug is . > Nobody is saying that, you're just moving goalposts now. Nobody is saying you should al…

See my comment to @Capricorn2481 here[0]. You seem to be saying that unsafe code can be made correct so as to ensure soundness. This can be done by verifying invariants in unsafe code, but it is generally discouraged, as it tempts large unsafe blocks that could be partially safe code. Both you and @Capricorn2481 don't seem to make the distinction between "arbitrary safe code" and "my safe code within the module with unsafe", which is a crucial part of the idea of writing encapsulated unsafe. Technically, I believe unsafe code can still be made sound in the presence of arbitrary safe code, but it would have to tiptoe around violating memory safety, lowering its utility and performance substantially, if not completely.

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

Re: Several core problems with Rust

#324
post #200

> In fact, for many applications malfunctioning is better than crashing — particulary in the embedded world where Rust wants to be present. Not a fact. Particularly in the embedded world, crashing is preferable to malfunctioning, as many embedded devices control things that might hurt people, directly or indirectly. > If a pacemaker stops — telling a victim “but the memory was not corrupted in the crash” is a weak co…

>Not a fact. Particularly in the embedded world, crashing is preferable to malfunctioning, as many embedded devices control things that might hurt people, directly or indirectly.

It really depends on how deeply Turing you mechanism is. By being "Turing" I mean "the behavior is totally dependant on every single bit of previous information". For a reliable system turing-completeness is unacceptable for separate functions i.e. it should produce a correct result in a finite amount of time no matter what hapened in the past. Particulary, that's why modern real-time systems cannot be fit into Turing machine, because Turing machine has no interrupts.

>If a pacemaker suddenly starts firing at 200Hz, telling a victim "but at least it didn't crash" is a weak consolation. A stopping pacemaker is almost always preferable to a malfunctioning one

You almost make an excuse for general unreliability of programs. Mainstream C is unreliable, C++ is unreliable, Rust is unreliable. I can agree that Rust is not less reliable than C/C++, but it is definitely less reliable than some other language e.g. BEAM-based ones. I mean in Rust standard library some time ago I actually read "in these and these conditions the following code will deadlock. But deadlock is not an undefined behavior, so it's ok". The designers of Rust did not really try to support any kind of "recover and continue" way of functioning. Yes, you can catch the panic, but it will irreversibly poison some data.

Re: Several core problems with Rust

#325
post #200

> In fact, for many applications malfunctioning is better than crashing — particulary in the embedded world where Rust wants to be present. Not a fact. Particularly in the embedded world, crashing is preferable to malfunctioning, as many embedded devices control things that might hurt people, directly or indirectly. > If a pacemaker stops — telling a victim “but the memory was not corrupted in the crash” is a weak co…

It's honestly mind boggling how people react to this. Rust turns unknown failures in C and C++ into known failures and suddenly the C/C++ people start caring about the failures, but attribute the failure to the new language, even though the same failures are secretly lurking in their C/C++ code bases. It's kind of like trying to silence a whistleblower. >Please read the whole article. If the unwrap hadn't caused an e…

>Rust turns unknown failures in C and C++ into known failures and suddenly the C/C++ people start caring about the failures

I'm actually the one who promotes paranoidal assert-s everywhere. I do agree the original statement from the article is ambiguous, probably should have written something like "memory safety in Rust does not increase reliability".

>The pacemaker argument is complete nonsense, because the pacemaker must keep working even if it crashes. You can forcibly induce crashes into the pacemaker during testing and engineer it to restart fast enough that it hits its timing deadline anyway.

I'm not sure whether there is a deadlock-free modification of Rust — deadlock is not considered an undefined behavior in Rust.

Re: Several core problems with Rust

#326
post #116

Earlier quoted context omitted.

> Some very solid argument here. However, as already implied in my article, you can get most of the guarantees without losing your sanity. I think this is part of why your article is causing a strong reaction from a lot of people; quite a lot of the justification for your point of view is left implied, and the concrete examples you do give about Rust (e.g. `Arc >>>` and `.unwrap`) are hard not to see as straw men whe…

Also the argument with `Arc >>>` boils down to "Rust makes it hard to work with automatic reference-counted shared mutable heap-allocated state." In which case... mission accomplished? Rust just made explicit all the problems that you still have to deal with in any other language, except dealing correctly with all that complexity is such a pain that you will do anything you can to avoid it. Again, mission f#*@ing acc…

>Maybe you DON'T need that state to be shared, reference-counted, or heap allocated. Maybe you can refactor your code to get rid of those annoyingly hard to deal with abstractions. And you end up with better, more reliable, likely faster code at the end of it.

That's the point 4 in my article — Rust is horrible for mutable shared state. However, in the modern CPU-based programming mutable shared state is like 70% of all the code, so you cannot just ignore it. It's not that CPU-s have to be like that, it's they hapened to be like that.

>there's a concurrency bug I never would have thought of! I guess all that old code I wrote has bugs that I didn't know about at the time.

Programming languages or libraries that excel at concurrency do not use the Arc> nuisance. At least they are not imposing it as a main tool. Having shared mutable state does not mean you directly change cell there, like you would in C/C++. I mean if you have a cyclic graph of connected objects — how the hell are you gonna employ Arc> for handling them? What Rust actually does is carving in stone pathologic C/C++ ways of "correct handling of shared mutable state" — whatever it is.

Re: Several core problems with Rust

#327
post #132

Earlier quoted context omitted.

>You can throw in a destructor [1]. You just need to mark that destructor noexcept(false). You do get a guaranteed std::terminate if an exception escapes a destructor while unwinding is already underway, though. Come on, please tell me you don't do this in your code. Formally you are correct, but there are many things in C++ that should have better not existed. >Why doesn't a similar argument apply to "specially desi…

> Come on, please tell me you don't do this in your code. I don't, but that's not to say that I think it should never be done. > Formally you are correct, but there are many things in C++ that should have better not existed. Sure, but I think it's important that one should do their best to be correct and/or precise. > Because it would lose most of the Rust properties by that time. Perhaps for specific bits of code, b…

>I'm curious how you came to that conclusion. It seems wrong both on a theoretical level (Drop/unwinding/catch_unwind should obviously suffice for at least some cases?) and on a practical level (tokio can recover from worker thread panics just fine?).

Tokio provides crash-resistant synchronization primitives, but it cannot recover complex structures you've been handling — you either need to write panic-handlers that would revert your data to more-or-less manageable state or employ ready-made libs that do it for you. Languages designed for crashes don't require ad-hoc recovering — they just automatically clean up everything. That's what I've called "viable".

I mean you could have written crash recovery in C++, but it's just not viable because of how many things can go wrong.

Re: Several core problems with Rust

#328

Earlier quoted context omitted.

> How would you fix this by only editing the code inside the unsafe block? In this case, the solution is to mark make_slice as unsafe. This should make sense if you think about it for a second, because it's a thin wrapper around an unsafe function. If it were possible to make a function which did what std::slice::from_raw_parts does safe, it wouldn't be marked as unsafe. > It's the exact opposite. From the Rust docs.…

> The actual use case for std::slice::from_raw_parts is for when you control the pointer and length and thus can guarantee that they're valid (specifically, by encapsulating them and making sure no safe functions can get them into an invalid state) Right...so you fix it from the safe code, because the safe code is in charge of using from_raw_parts correctly. > Either way, the memory safety bug is not in safe rust Thi…

> Right...so you fix it from the safe code, because the safe code is in charge of using from_raw_parts correctly.

No, you fix it from the unsafe code (just not your unsafe code, since the API you're trying to expose is not possible to make safe). You encapsulate the arguments to from_raw_parts, provide a safe interface, and only manipulate the encapsulated data inside other unsafe blocks.

> This might just be a difference of semantics between us, but I really don't see how you can arrive at that conclusion. Just remove the function entirely and let's use from_raw_parts directly. The bug is literally not in unsafe, it's when we drop the vec after making a slice from it.

I think you're correct that semantics are at the core of our disagreement. Specifically, there are three different possible ways to answer the question of where a bug is:

1. Where the bug manifests a problem (produces an incorrect value, segfaults, etc).

2. Where the bug is directly caused.

3. Where the incorrect code is.

In the code you provided, 1 happens at the println, 2 happens at the drop, but 3 happens in the unsafe block. All your safe code is correct according to rust's memory safety rules rules (which is why it would pass the borrow checker), the problem is that by using an unsafe block you promised to uphold those rules yourself, and have failed to do so.

Re: Several core problems with Rust

#329
post #304

Earlier quoted context omitted.

That’s a very good quote. There’s a handful of pain points that the Rust model does impart which are not fundamental. For example, unsafe code is required to get a mutable borrow to two different fields of a struct at the same time. But really that’s the only example I can think of offhandedly, and I expect there are not many in total. Nearly all of the pain is merely the trouble of thinking through these issue upfro…

The only other one I can think of is kind of poor ergonomics around "self-borrows"; it's a lot less common than I've felt the need to mutably borrow two fields from the same struct independently, but there have very occasionally been times where I've realized the simplest way to structure something would be to have one field in a struct borrow another. This is somewhat hard to express given the need to initialize all…

>the things that Rust prevents me from handling safely because of its own limitations rather than them being fundamentally unsafe are quite rare, and even with those, they tend to be things that I would be quite likely to mess up in practice if I tried to do them in a language without the same safety rails.

Any language with GC can handle complex links between object, including mutable object. Like Erlang/Elixir, JS, Go, etc. You message implies runtime-less language, but majority of practically employed languages are runtime-full.

Re: Several core problems with Rust

#330
post #102

Earlier quoted context omitted.

>Yeah until that memory safety issue causes memory corruption in a completely different area of code and suddenly you're wasting time debugging difficult-to-diagnose crashes once they do start to surface. Some very solid argument here. However, as already implied in my article, you can get most of the guarantees without losing your sanity. Memory-safety-related problems are important, but they are not the sole source…

> It might be counterintuitive, but garbage collectors in multithreaded code can be very efficient. What has garbage collector to do with multithreaded code? Once you have two or more threads which needs to share data, they need to sync and you'd end up using some kind of lock, which will affect the performance. GC doesn't make anything efficient or less efficient here. It might make the code simpler as you don't hav…

For some reason people from C/C++ world seem to be oblivious to persistent data structures. Those don't need locks, you just swap a single pointer.
Post reply on HN