Live data from Hacker News

The borrowchecker is what I like the least about Rust

viralinstruction.com

271–280 of 459 posts

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

#271

Earlier quoted context omitted.

Typescript is not a language that matches intuition. Typing complex code while avoiding the any hatch resembles fighting limitations of the borrow checker in Rust.

I find it highly highly intuitive. But my background is haskell like languages. I feel with practice basic type checking is something that helps you rather then hinders you. It can be learned easily imo. People coming from js tend to have a hard time but that's understandable. The borrow checker is not easily learned imo. It's always me running into a wall.

For me the problem with TypeScript or Flow when the latter was a thing was that the syntax/semantics of the sub language of types was extremely ad-hoc with so many idiosyncrasies. Maybe if I programmed it all the time I would learned it. But I had to change the relevant code only occasionally and typing helpers to access DOM required constant look at the spec and StackOverflow.

With Rust the rules at least are simple. While following them can be a struggle the compiler errors at least are much more helpful and points to the problem with the design or the checker limitations.

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

#272

Earlier quoted context omitted.

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 comparison is contrived precisely because he's comparing it to other memory-safe languages. The borrow checker was introduced because the goal of the language was a memory safe language without a runtime. Falling back to indices isn't "ironic", its exactly how you would solve the problem in C/C++. If your argument is "well Rust should be like Julia and have a GC", well thats not Rust. That language also exists, i…

Rust have few garbage collectors[1] to chose from. For example, rust-cc is simple to use, just put #[derive(Trace, Finalize)] on struct.

[1]: https://crates.io/keywords/garbage-collector

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

#273

Earlier quoted context omitted.

People sometimes say this like it's a dunk, but there's a finite amount of complexity any given programming task can shoulder, you have to allocate it somehow between the programming environment, the problem domain you're working on, and the algorithmic sophistication you bring to bear on that problem, and it's not clear to me what the benefit is in allocating more than you need to your programming language. A lot of…

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!

I worked with C# for a decade and it's become a really great general purpose language. I'd still prefer never to work with it ever again after having worked with Go. This isn't for technical reasons at all, but because Golang is so easy to work with for "people reasons". There are brilliant parts of Go, but the only thing I find myself missing in other languages is the simplistic module isolation, where every folder is a module, every file within the folder is part of it and then you expose functions with capital letters at the beginning of their name. Holy hell did I wish Python had that. Anyway, the thing that makes Go nice to work with over time is the explicity of everything and a lot of the very opinionated decisions. With a piece of Go code I can jump in and immediately know what is going on regardless of who wrote it. With C# I'll often have to go down long "go to definition" paths. Often you will end up trying to figure out just how someone was trying to "fight" the implicit magic of the non-STL Microsoft dependencies they used. Usually because they didn't really understand what they were doing. All of these are human issues and no fault of C# or .Net as such.

Of the few technical advantages Go had for us is that we don't need a single dependency outside of the standard library, which can't live in isolation. We use SQLC and Goose, both are run in containers that only have rights and access on the development side of things.

I'm not sure I would say that Golang has a lot of weird, pointless quirks, but it has opinions and if you happen to dislike them, well... that sucks. I hate the fact that they didn't want runtime assertions as an example, so it's not like I don't understand why people dislike Go for various reasons. I've just accepted that those strong opinions is the reason Go is so productive.

The challenge for us is that it's not exactly as productive as Python. So while you'll need to do a lot of toolchain work to get Python anywhere near Go's opinionated stucture, that is often a better choice if you're not a hardcore software engineering team. At least for us, it's been much easier to get our business intelligence people to adopt UV, pyrefly, ruff and specific VSC configs for their work than to have them learn Go.

I suspect that is why Rust is also doing so well. Go is a better Java/C# for a lot of places, but are you really going to replace your Java/C# with Go if you have dacades worth? If you're not coming from Java/C# will you really pick Go over Rust? I'm not sure, but I do know, Go failed to become a Python replacement for us. It did replace our C#, but we didn't have a lot of C#. Eventually we'll likely replace our Go with C/Zig and Python to keep the language count lower.

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

#274
post #167

Earlier quoted context omitted.

Something like the following? I am trying and failing to reproduce the issue, even with mutable AST nodes. use bumpalo::Bump; use std::io::Read; fn main() { let mut arena = Bump::new(); loop { read_and_process_lines(&mut arena); arena.reset(); } } #[derive(Debug)] enum AstNode { Leaf(&'a str), Branch { line: &'a str, meta: usize, cons: &'a mut AstNode }, } fn read_and_process_lines(arena: &Bump) { let cap = 40; let b…

I checked my repo history and never committed because I failed to get it working. I don’t recall my issues. If you can get a full JSON parser working then maybe I’m just wrong. Arrays, objects with keys/values, etc. I’d like to think I’m a decent Rust programmer. Maybe I just need to give it another crack and if I fail again turn it into a blog post…

oxc_parser uses bumpalo (IIRC) to compile an AST into arena from a string. I think the String is outside the arena though, but their lifetimes are "mixed together" into a single 'a, so lifetime-wise it's the same horror to manage. But manage they did.

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

#275
post #151

There are some artificial limitations, but I love the upside: I don't need defensive programming! When my function gets an exclusive reference to an object, I know for sure that it won't be touched by the caller while I use it, but I can still mutate it freely. I never need to make deep copies of inputs defensively just in case the caller tries to keep a reference to somewhere in the object they've passed to my funct…

Yes! One of the worst bugs to debug in my entire career boiled down to a piece of Java mutating a HashSet that it received from another component. That other component had independently made the decision to cache these HashSet instances. Boom! Spooky failure scenarios where requests only start to fail if you previously made an unrelated request that happened to mutate the cached object.

This is an example where ownership semantics would have prevented that bug. (references to the cached HashSets could have only been handed out as shared/immutable references; the mutation of the cached HashSet could not have happened).

The ownership model is about much more than just memory safety. This is why I tell people: spending a weekend to learn rust will make you a better programmer in any language (because you will start thinking about proper ownership even in GC-ed languages).

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

#276
post #8

This post pretty much completely ignores the advantages of the borrow checker. I'm not talking about memory safety, which is it's original purpose. I'm talking about the fact that code that follows Rust's tree-style ownership pattern and doesn't excessively circumvent the borrow checker is more likely to be correct . I don't think that was ever the intent behind the borrow checker but it is definitely an outcome. So…

> is more likely to be correct. This is a moot statement. Here is a thought experiment that demonstrates the pointlessness of languages like Rust in terms of correctness. Lets say your goal is ultimate correctness - i.e for any possible input/inital state, the program produces a known and deterministic output. You can chose 1 of 2 languages to write your program in: First is standard C Second is an absolutely strict…

This isn't true in practice. People don't write impossibly comprehensive test suites in C, and they don't use extremely loose types in Rust either.

It really does matter which language you choose if you want correct code.

> programming in something like C is going to be more efficient, whereas the second language will force you write a lot more code for basic things.

Like how string manipulation is so much simpler and easier in C compared to Rust? Hmm.

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

#277

Earlier quoted context omitted.

People sometimes say this like it's a dunk, but there's a finite amount of complexity any given programming task can shoulder, you have to allocate it somehow between the programming environment, the problem domain you're working on, and the algorithmic sophistication you bring to bear on that problem, and it's not clear to me what the benefit is in allocating more than you need to your programming language. A lot of…

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 surprise if you have worked in a lower level language but might be a surprise if you come from a language where that isn’t the case.

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

#278
post #179

Earlier quoted context omitted.

Due to lack of many abstractions, and lack of exceptions, Go is a less concise Java. It's a language where the lack of expressiveness forces you to write simpler code. (Not that it helps too much.) Go's selling points are different: it takes a weekend to learn, and a week to become productive, it has a well-stocked standard library, it compiles quickly, runs quickly enough, and produces a single self-contained execut…

People sometimes say this like it's a dunk, but there's a finite amount of complexity any given programming task can shoulder, you have to allocate it somehow between the programming environment, the problem domain you're working on, and the algorithmic sophistication you bring to bear on that problem, and it's not clear to me what the benefit is in allocating more than you need to your programming language. A lot of…

There's no free lunches. But I could take a problem, make it arbitrarily harder, then take that arbitrary limit away. If someone thought they had to solve the harder problem, and found out they only have to solve the easier one, did they get a free lunch?

This isn't meant to be allegorical or anything specific. I guess it's just an observation that sometimes your lunch can be cheaper than you thought.

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

#279
post #70

One of his examples of a borrow checker excess: struct Id(u32); fn main() { let id = Id(5); let mut v = vec![id]; println!("{}", id.0); } isn't even legit in modern C++. That's just move semantics. When you move it, it's gone at the old name. He does point out two significant problems in Rust. When you need to change a program, re-doing the ownership plumbing can be quite time-consuming. Losing a few days on that is…

Rust borrow checker is designed to enforce "one owner" model (a tree). When you need to have more than one reference, you can use Rc + Weak[0]. Example DoubleLinkedList implementation:

  struct Node {
    pub data: T,
    pub prev: Option>>>,
    pub next: Option>>>,
  }
Moreover, if you have cycles instead of trees, you can use a garbage collector with support for cycles, like rust-cc[1].

So yes, it's cannot be done statically, because Rust is not designed for that.

However, problem disappears when 'static lifetime is used (or arenas). Nodes can be marked as deleted, instead of dropping them, so pointers are always valid.

In same vein, when nodes are deleted rarely, they can be simply marked as deleted, without dropping them completely (until sibling nodes are updated, at least):

  struct Node {
    pub data: Option,
    pub prev: Option>>>,
    pub next: Option>>>,
  }
When node is deleted (its payload is dropped), linked list is still walkable.

[0]: https://doc.rust-lang.org/std/rc/struct.Weak.html

[1]: https://github.com/frengor/rust-cc

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

#280
post #241

Earlier quoted context omitted.

Non-UB data structure corruption and other incorrect behavior isn't like, super obviously better than UB corruption and other incorrect behavior.

The obvious upside is that it's so much easier to debug when there's no UB. Debugging UB is never enjoyable.

It’s pretty common to implement graphs in terms of arrays, not because of indices but because of cache locality.

So your “UB” and “non-UB” code would look effectively identical to the CPU and would take the same amount of debugging.

The reality is whether an index was tombstones and referenced or “deallocated” and referenced it is still a programmer fault that is a bug that the compiler could not catch

Post reply on HN