Live data from Hacker News

The borrowchecker is what I like the least about Rust

viralinstruction.com

331–340 of 459 posts

Re: The borrowchecker is what I like the least about Rust

#331

Earlier quoted context omitted.

Ummm. Yeah it is. I'm sure you can come up with some cases where the impact of the bugs is roughly equivalent, but generally speaking, UB is a terrible class of bug. For example, if the aho-corasick crate accidentally tries to look up a dangling state node, you'll get a panic. But if this were UB instead, that could easily lead to a security problem or just pretty much anything... because behavior is undefined . So g…

Author here. In scientific computing, there isn't much of a security implication of UB, and the most dangerous kind of error are when your program silently computes the wrong thing. And that is especially likely when you use indices manually. You could say that UB enables all behaviour, including silently wrong answers, and you'd be right. But it's more likely to crash your program and therefore be caught. Most impor…

For the record, I completely agree that using GC is appropriate for problems that deal with possibly-cyclic general graphs, and maybe even wrt. DAG structures whenever one wishes to maximize throughput and the use of simpler refcounting would involve heavy overhead. (This is a well-known pitfall wrt. languages like Swift, that enforce pervasive use of atomic reference counting.)

Since the use of "pluggable" GC in C-like or Rust-like languages is still uncommon, this generally means resorting to a language which happens to inherently rely on GC, such as Golang.

Re: The borrowchecker is what I like the least about Rust

#332

Earlier quoted context omitted.

But gorutines are better than threads when you need you need many (say >100K) of them. Lightweight threads are not unique to Go but in Go they are easy to use and the default way to build network applications. Java is catching up with project Loom but it would never be as easy as Go to use.

> are better than threads when you need many (say >100K) of them Stackless coroutines are even more efficient for that case, and Rust makes them comparatively easy. (And slated to get even easier in future versions, as the async support is improved further.) Moreover stackful coroutines/fibers as used in Golang also makes it infeasible to have seamless FFI with the standard C API/ABI, which cuts you off from the bulk…

Alef (arguably one of the antecedent of Go) had stackful couroutines/fibers yet it could have had such a seamless FFI to the standard C API/ABI.

The plan9 libthread (which reimplemented the Alef concurrency model for C) did have seamless use of the C API/ABI.

The mechanism was that it had threads and coroutines, a function needing to make use of the C API in a seamless manner would simply run as a thread rather than a coroutine. It was then easy to share data as the CSP model (with channels) worked between both coroutines, threads, and coroutines within a thread.

So if one wishes to use stackful coroutines, and still have that seamless compatibility, an approach mixing the Go and Alef approaches would seem necessary. i.e. the Go migrating coroutines as a default, but with the option to use Alef like thread bound coroutines when necessary.

Re: The borrowchecker is what I like the least about Rust

#333

Earlier quoted context omitted.

> are better than threads when you need many (say >100K) of them Stackless coroutines are even more efficient for that case, and Rust makes them comparatively easy. (And slated to get even easier in future versions, as the async support is improved further.) Moreover stackful coroutines/fibers as used in Golang also makes it infeasible to have seamless FFI with the standard C API/ABI, which cuts you off from the bulk…

Alef (arguably one of the antecedent of Go) had stackful couroutines/fibers yet it could have had such a seamless FFI to the standard C API/ABI. The plan9 libthread (which reimplemented the Alef concurrency model for C) did have seamless use of the C API/ABI. The mechanism was that it had threads and coroutines, a function needing to make use of the C API in a seamless manner would simply run as a thread rather than…

> The mechanism was that it had threads and coroutines, a function needing to make use of the C API in a seamless manner would simply run as a thread rather than a coroutine.

This reintroduces the colored functions that we were trying to get away from in the first place, by adopting stackful coroutines/fibers. Why not use async at that point? I can understand that Alef didn't, because stackless mechanisms were not well understood at the time. But it's plausible that we can do better.

Re: The borrowchecker is what I like the least about Rust

#334

Earlier quoted context omitted.

Ummm. Yeah it is. I'm sure you can come up with some cases where the impact of the bugs is roughly equivalent, but generally speaking, UB is a terrible class of bug. For example, if the aho-corasick crate accidentally tries to look up a dangling state node, you'll get a panic. But if this were UB instead, that could easily lead to a security problem or just pretty much anything... because behavior is undefined . So g…

Author here. In scientific computing, there isn't much of a security implication of UB, and the most dangerous kind of error are when your program silently computes the wrong thing. And that is especially likely when you use indices manually. You could say that UB enables all behaviour, including silently wrong answers, and you'd be right. But it's more likely to crash your program and therefore be caught. Most impor…

You're engaging in a motte-and-bailey fallacy. Your motte is your much more narrow claim about Rust's advantages in a context where you claim not to care about undefined behavior as a class of bugs more severe than logic errors. And you specifically make this claim in an additional context where a GC language is appropriate. That's an overall pretty easy position to defend, and if that were clearly the extent of it, I probably wouldn't have responded at all.

But, that context has been dropped in this thread, and instead folks are making very general claims outside of that very restricted context. Moreover, your bailey is that you don't do a good job outlining that context either. Your blog's opening paragraphs mention nothing about that more constrained context and instead seem to imply a very general context. This is much harder to defend. You go on to mention scientific computing, but instead of it being a centerpiece of the context of your claim, it's just mentioned as aside. Instead, your blog appears to be making very broad claims. But you've jumped in here to narrow them significantly, to the point that it materially changes your point IMO. Let's just look at what you said here:

> The first time someone gave be this advice, I had to do a double take. The Rust community's whole thing is commitment to compiler-enforced correctness, and they built the borrowchecker on the premise that humans can't be trusted to handle references manually. When the same borrowchecker makes references unworkable, their solution is to... recommend that I manually manage them, with zero safety and zero language support?!? The irony is unreal. Asking people to manually manage references is so hilariously unsafe and unergonomic, the suggestion would be funny if it wasn't mostly sad.

There's no circumspection about the context. You're just generally and broadly dismissing this entirely as if it weren't a valid thing ever. But it absolutely is a valid technique and it has real practical differences with an approach that uses raw pointers. If the comparison is with a GC and that context is made clear, then yes, absolutely, the comparison point changes entirely! If you can abide a GC, then a whole bunch of things get easier... at some cost. For example, I don't think it's possible to write a tool like ripgrep with its performance profile in a GC language. At least, I've never seen it done.

Re: The borrowchecker is what I like the least about Rust

#335
post #277

Earlier quoted context omitted.

Unfortunately, Golang has a whole lot of weird, pointless quirks in both its base language and standard library, compared to something with a more elegant and from-the-ground-up design, like Rust itself or perhaps OCaml/ReasonML. Not very good news if you want the language to just "get the hell out of your way". I suppose it's still way better than the "enterprise" favored alternative of Java/C# though!

> Golang has a whole lot of weird, pointless quirks in both its base language and standard library Having used go in anger I don’t necessarily agree with this, could you point out an example. Maybe I just accepted it and work around it without paying much attention. If you are referring to interface types being able to be null, well they are allocated on the heap and have dynamic dispatch, this isn’t particularly a s…

Here’s a great read on its quirks, and where it really isn’t “simple”: https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-...

https://dave.cheney.net/2014/03/19/channel-axioms

Re: The borrowchecker is what I like the least about Rust

#336

Earlier quoted context omitted.

> OP's argument that this approach violates the very reason the borrowchecker was introduced in the first place. No it doesn't. I just don't think author understands the pitfalls of implementing something like a graph structure in a memory unsafe language. The author doesn't write C so I don't believe he has struggled with the pain of chasing a dangling pointer with valgrind. There are plenty of libraries in C that e…

Whoah, hold on, the author isn't comparing writing graph structures in Rust to writing it in memory-unsafe languages --- they're comparing it to writing it in other memory-safe languages . You can't force a false dichotomy between Rust and C to rebut them.

The author is also suggesting that a data structure consisting of a sea of objects referencing each other works perfectly in Python. It doesn’t — Python’s GC can only collect it as a cycle, and this path is not immediate the way the refcount is. And you’re paying for refcounts. Even in a language with full tracing GC (Java, etc), you’re not winning any performance points by making a ton of objects all referencing each other.

Indices or arenas or such will result in better code in all these languages.

Re: The borrowchecker is what I like the least about Rust

#337

Earlier quoted context omitted.

> isn't even legit in modern C++. That's just move semantics. When you move it, it's gone at the old name. Exactly the opposite actually. Rust has destructive move while modern C++ has nondestructive move. So in Rust, an object is dead after you move out of it, and any further attempts to use it are a compiler diagnosed error. In contrast, a C++ object is remains alive after the move, and further use of it isn't forb…

Indeed, this is strictly worse than rust. The object is alive but in an invalid state, so using it is a bug but not one the compiler catches. In the worse case the move is only destructive for larger objects (like SSO), so your tests can pass and you've still got a bug.

Or when you enable optimizations and since accessing an object with invalid state is UB, the compiler helpfully decides it is now permitted to format your hard drive.

https://gcc.gnu.org/legacy-ml/gcc/2016-02/msg00381.html

"The fact is, undefined compiler behavior is never a good idea. Not for serious projects."

Re: The borrowchecker is what I like the least about Rust

#338

I don't use Rust much, but I agree with the thrust of the article. However, I do think that the borrowchecker is the only reason Rust actually caught on. In my opinion, it's really hard for a new language to succeed unless you can point to something and say "You literally can't do this in your language" Without something like that, I think it just would have been impossible for Rust to gain enough momentum, and also…

Agreed. As a comparison Golang was sold as "CSP like Erlang without the weird syntax" but people realized channels kind of suck and goroutines are not really a lot better than threads in other languages. The actual core of OTP was the supervisor tree but that's too complicated so Golang is basically just more concise Java. I don't think this is a bad thing but it's a funny consequence that to become mainstream you ha…

Ahh finally someone has said it. Unfortunately I can't seem to voice this opinion without getting the critique of you're just not smart enough to get it.

While I like the language, threads in Go are not any easier than any other language (which is to say, most devs can't use them correctly, and your program will have bugs), and suffer from a ton of ergonomic issues, like being hard to keep track of, difficult(ish) cancellation(how do you cancel a big synchronous I/O operation), and channels suffer from backpressure related hard-to-debug issues.

Re: The borrowchecker is what I like the least about Rust

#339
post #302
post #155

Earlier quoted context omitted.

[flagged]

Serious being what? Most programmers are "lousy programmers" because most of the are not researchers and have no interest in theory. I can write in Go or Rust (or anything else given some time) but I won't ever use Rust to write any kind of business logic for my company - and this is what I do most of the time I actually write code. Why? Because Rust is terrible for the job (at least if we are talking about real life…

I can do business logic, like complex CRUD, in Rust much faster than in other languages. I assume, «lousy programmers» have problems with separation of concerns, which then cause problems with borrow checker, because developers need to perform different things in different ways on different parts of data structures (AKA «god object» anti-pattern).

Re: The borrowchecker is what I like the least about Rust

#340
> They might argue that the snippets don't show there is any real ergonomic problem, because the solutions to make the snippets compile are completely trivial. In the last example, I could just derive Clone + Copy for Id.

I’ll bite a little bit. If I write:

    struct Thing(u32);
Then I need to make a choice: is Thing a plain value that its holders can freely clone or is Thing an affine object that is gone when consumed? I think it’s great that Rust makes this explicit, even if it’s a tiny bit odd that one option is a default and the other option is rather verbose. If I could have a unicorn, too, I’d also like to be able to request “linear” behavior, i.e. disallow code that fails to consume the object.

Sure, I suppose someone could invent a language where an LLM reads the type definitions and fills in the blanks as to what uses are valid, but this seems like a terrible idea. Or one could use a language without affine types (most languages, and even C++’s move feature entirely fails to enforce any sort of good behavior).

Post reply on HN