Hacker News new | ask | show | jobs
by snazz 2087 days ago
Although I haven't verified this myself—I'm not much of a Rust wizard—I've heard that Rust's type system and borrow checker allows you to spend less time total, because your debugging time drops significantly.
5 comments

Verifying that would be incredibly difficult/impossible but I've personally found that rust has on multiple occasions prevented me from making a mistake that would have cost likely hours to debug that I was instead able to fix in a few minutes.
Curiously, I’ve never seen anyone in forums take this premise to its logical conclusion and advocate the use of a language which has a much more powerful type system than Rust does, like dependent or refinement types. Haskell, Scala, Idris, and others (even Python with crosshair!) natively or with extensions support enforcing some very sophisticated constraints on a program. Tagged unions and interfaces are hardly the limit of what’s possible. However, I don’t think cutting edge type systems, theorem provers or other sophisticated tools for program validation have demonstrated enough business value to receive widespread adoption outside of only some (albeit very important) market sectors.

Really, I think most argumentation comes down to “the type system of the programming language I want to use anyway is the right one, and everything else is wrong”.

I lump this into the category of learning how to best apply your tools to the right situations.

If I'm slinging together an internal CRUD app, the extra time thinking about types probably will slow me down because the major risks aren't really type problems.

But if for example I'm working on something in a game engine, being able to use the language and compiler to enforce assumptions/invariants at compile time is really useful.

Perhaps compared to Python, Ruby, and vanilla ES6; there is a class of epsilon-from-typo bugs that modern statically typed languages catch and that force rigorous Python and Ruby shops to waste time writing vast batteries of tests for the most banal functionality in their codebase ("does this setter set?").

Moving from one of those languages to a modern statically typed language can come with a feeling that your programs tend to run successfully as soon as they compile --- certainly they do more often than they would in Ruby. Because the compiler is doing some of that testing work for you. It's a good feeling.

But it hasn't been my experience that the extra rigor in Rust's model produces the same boost from Java or Go; in fact, my first-run experience with Rust is generally less reliable than Go; there are a few more things that can go wrong with a Rust program and slip by the compiler.

Which is what you'd expect; Rust isn't garbage collected, so the programmer has to do the garbage collection, and every program is responsible in some sense for designing and implementing that part of its own runtime. There are things you can get wrong there that GC'd languages don't make you bother trying to get right. And, of course, if you take the time to get your per-program runtime really right, there's a performance gain to be had from doing so. It's all tradeoffs.

> Which is what you'd expect; Rust isn't garbage collected, so the programmer has to do the garbage collection, and every program is responsible in some sense for designing and implementing that part of its own runtime.

This seems like a really odd thing to say in at least two dimensions. Firstly, I would say that "the programmer has to do the garbage collection" isn't an accurate way to describe Rust. Secondly, and particularly in comparison with Go, Rust's RAII applies more universally than Go's garbage collection. How many times have you seen a missing defer or a defer inside of a loop? I've seen both quite a bit. But those bugs basically don't happen in Rust precisely because the programmer almost never needs to deal with destruction directly. It's done automatically by the compiler.

As to the broader point, I would very strongly oppose the notion that Rust is less reliable than Go. While I don't think I would argue that Rust gives the same boost over Go that, say, Java or Go give over Ruby (that's a tough argument to make anyway, even if I agreed with it), I would say there is a meaningful boost. But that's a tough argument to make too. Aside from the obvious bits (nil errors and forgetting to check errors), for me, it basically comes to my belief that Go punishes its practitioners for abstraction. I wrote a bit more about that here: https://users.rust-lang.org/t/what-made-you-choose-rust-over...

The RAII vs. defer thing is a point well taken. Also, the kind of thing that doesn't show up in the totally-informal "how many of my post-hoc tests pass the first time I get them to compile" metric.

Another point, which to your credit you didn't make but which is nonetheless true, is that I am not a good Rust programmer!

I don't think Rust is less reliable than Go in the large. I do think there are more things you have to get right in a Rust program than in a Go program, so it's "less reliable" in that weird twilight state when you're first bringing up your program.

I do agree that Go punishes practitioners for abstraction. If you're, like, you, that's a very bad thing. If you're working with a team of people for whom the project is a means to an end and not a brilliant-cut gemstone, putting the brakes on abstraction can be a good thing, which I think a lot of Rust programmers will quickly learn after the nth time they've had to do an edit-compile cycle just to `let () = something` to figure out a type.

FWIW, my experience when it comes to Go and its abstraction limitations _is_ in the context of collaborating with others on a team at work. That's where we feel the limitations of abstraction pretty acutely. Because it means we, to a lesser degree, cannot build APIs that are harder to misuse.

The point about having too much power is taken, and I don't have experience with that in a team context. And yeah, I am not exploring the Rust downsides as much here, and there are definitely others.

> I would say that "the programmer has to do the garbage collection" isn't an accurate way to describe Rust.

Although you could characterize it as "Rust makes the programmer think about and describe the garbage collection."

Fans of static type checking use that as a rebuttal against "but I'm so much more productive in $dynamically_typed_language". I've also seen it in discussions about Scala, Haskell, TypeScript, etc.

Intuitively it makes a lot of sense, and it jives with my subjective feelings (especially for longer-lived projects), but I'm not sure if there have been any studies to back up that claim.

From an organizational perspective, the benefits of static type checking are quite obvious. I'd go so far as to say it's a software engineering best practice to prefer languages with static types when building new software. The reason for this is because many, many, many engineering hours are saved at an organizational level by having a compiler that is able to enforce correct data structures throughout the application. Eschewing types for speed is just a form of technical debt that has to be repaid in perpetuity whenever it comes time to maintain or refactor the software, not to mention the fact that every new developer working on the application is now burdened with this debt.
I am not aware of any studies that definitively show an advantage for static or dynamic typing; there have been ones that didn't provide anything conclusive, and you could argue that the experiment design wasn't representative.

https://danluu.com/empirical-pl/ is a good place to get started on this topic.