Live data from Hacker News

Thoughts on Go vs. Rust vs. Zig

sinclairtarget.com

231–240 of 599 posts

Re: Thoughts on Go vs. Rust vs. Zig

#231
post #222

Earlier quoted context omitted.

> languages where "trivial" things "just require" rapidly become "not so trivial" in the aggregate Sure. And in C and Zig, it's "trivial" to make a global mutable variable, it "just requires" you to flawlessly uphold memory access invariants manually across all possible concurrent states of your program. Stop beating around the bush. Rust is just easier than nearly any other language for writing concurrent programs,…

This is a miscommunication between the values of “shipping” which optimizes for fastest time to delivery and “correctness” which optimizes for the quality of the code. Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP. You can get away with writing incorrect concurrent programs in other languages… for a while. And sometimes that’s what bus…

> Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP.

I don't find that to be the case. It may be slower for a month or two while you learn how to work with the borrow checker, but after the adjustment period, the ideas flow just as quickly as any other language.

Additionally, being able to tell at a glance what sort of data functions require and return saves a ton of reading and thinking about libraries and even code I wrote myself last week. And the benefits of Cargo in quickly building complex projects cannot be overstated.

All that considered, I find Rust to be quite a bit faster to write software in than C++, which is probably it's closest competitor in terms of capabilities. This can be seen at a macro scale in how quickly the Rust library ecosystem has grown.

Re: Thoughts on Go vs. Rust vs. Zig

#232
post #222
post #214

Earlier quoted context omitted.

> [...] is trivial in Rust [...] it just requires [...] This is a tombstone-quality statement. It's the same framing people tossed around about C++ and Perl and Haskell (also Prolog back in the day). And it's true, insofar as it goes. But languages where "trivial" things "just require" rapidly become "not so trivial" in the aggregate. And Rust has jumped that particular shark. It will never be trivial, period.

> languages where "trivial" things "just require" rapidly become "not so trivial" in the aggregate Sure. And in C and Zig, it's "trivial" to make a global mutable variable, it "just requires" you to flawlessly uphold memory access invariants manually across all possible concurrent states of your program. Stop beating around the bush. Rust is just easier than nearly any other language for writing concurrent programs,…

Rust is a 99% solution to a 1% problem.

Re: Thoughts on Go vs. Rust vs. Zig

#233
post #177

Earlier quoted context omitted.

No, this is still misunderstanding. Overcommit means that the act of memory allocation will not report failure, even when the system is out of memory. Instead, failure will come at an arbitrary point later, when the program actually attempts to use the aforementioned memory that the system falsely claimed had been allocated. Allocating all at once on startup doesn't help, because the program can still fail later when…

Which is why I said "allocate and pin ". POSIX systems have mlock()/mlockall() to prefault allocated memory and prevent it from being paged out.

Random curious person here: does mlock() itself cause the pre-fault? Or do you have to scribble over that memory yourself, too?

(I understand that mlock prevents paging-out, but in my mind that's a separate concern from pre-faulting?)

Re: Thoughts on Go vs. Rust vs. Zig

#234
post #76

I think the Go part is missing a pretty important thing: the easiest concurrency model there is. Goroutines are one of the biggest reasons I even started with Go.

> the easiest concurrency model there is Erlang programmers might disagree with you there.

Erlang is great for distributed systems. But my bugbear is when people look at how distributed systems are inherently parallel, and then look at a would-be concurrent program and go, "I know, I'll make my program concurrent by making it into a distributed system".

But distributed systems are hard. If your system isn't inherently distributed, then don't rush towards a model of concurrency that emulates a distributed system. For anything on a single machine, prefer structured concurrency.

Re: Thoughts on Go vs. Rust vs. Zig

#235
post #60

> In Go, a slice is a fat pointer to a contiguous sequence in memory, but a slice can also grow, meaning that it subsumes the functionality of Rust’s Vec type and Zig’s ArrayList. Well, not exactly. This is actually a great example of the Go philosophy of being "simple" while not being "easy". A Vec has identity; the memory underlying a Go slice does not. When you call append(), a new slice is returned that may or ma…

> There's also no way to shrink the memory underlying a slice.

Sorry, that is incorrect: https://pkg.go.dev/slices#Clip

> It's a common newbie mistake to think they do work like that, and write "append(s, ...)" instead of "s = append(s, ...)". It might even randomly work a lot of the time.

"append(s, ...)" without the assignment doesn't even compile. So your entire post seems like a strawman?

https://go.dev/play/p/icdOMl8A9ja

> So (generalizing) Go won't implement a feature that makes mistakes harder, if it makes the language more complicated

No, I think it is more that the compromise of complicating the language that is always made when adding features is carefully weighed in Go. Less so in other languages.

Re: Thoughts on Go vs. Rust vs. Zig

#236
post #222

Earlier quoted context omitted.

> languages where "trivial" things "just require" rapidly become "not so trivial" in the aggregate Sure. And in C and Zig, it's "trivial" to make a global mutable variable, it "just requires" you to flawlessly uphold memory access invariants manually across all possible concurrent states of your program. Stop beating around the bush. Rust is just easier than nearly any other language for writing concurrent programs,…

This is a miscommunication between the values of “shipping” which optimizes for fastest time to delivery and “correctness” which optimizes for the quality of the code. Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP. You can get away with writing incorrect concurrent programs in other languages… for a while. And sometimes that’s what bus…

Lately rust is my primary language, and I couldn't agree more with this.

I've taken to using typescript for prototyping - since its fast (enough), and its trivial to run both on the server (via bun) or in a browser. The type system is similar enough to rust that swapping back and forth is pretty easy. And there's a great package ecosystem.

I'll get something working, iterate on the design, maybe go through a few rewrites and when I'm happy enough with the network protocol / UI / data layout, pull out rust, port everything across and optimize.

Its easier than you think to port code like this. Our intuition is all messed up when it comes to moving code between languages because we look at a big project and think of how long it took to write that in the first place. But rewriting code from imperative language A to B is a relatively mechanical process. Its much faster than you think. I'm surprised it doesn't happen more often.

Re: Thoughts on Go vs. Rust vs. Zig

#237

Earlier quoted context omitted.

Nah, you just need to use `map_err` or apply a '.context' which I think anyhow can do (and my crate, `uni_error` certainly can otherwise).

I'm pretty familiar with the idiom here and I don't find error/result mapping fluent-style patterns all that easy to read or write. My experience is basically that you sort of come to understand "this goo at the end of the expression is just coercing the return value into whatever alternate goo the function signature dictates it needs", which is not at all the same thing as careful error handling. Again: I think Rust…

> you sort of come to understand "this goo at the end of the expression is just coercing the return value into whatever alternate goo the function signature dictates it needs", which is not at all the same thing as careful error handling.

I think the problem is Rust does a great job at providing the basic mechanics of errors, but then stops a bit short.

First, I didn't realize until relatively recently that any `String` can be coerced easily into a `Box` (which should have a type alias in stdlib lol) using `?`, so if all you need is strings for your users, it is pretty simple to adorn or replace any error with a string before returning.

Second, Rust's incomplete error handling is why I made my crate, `uni_error`, so you can essentially take any Result/Error/Option and just add string context and be done with it. I believe `anyhow` can mostly do the same.

I do sorta like Go's error wrapping, but I think with either anyhow or my crate you are quickly back in a better situation as you gain compile time parameter checking in your error messages.

I agree Rust has over complicated error handling and I don't think `thiserror` and `anyhow` with their libraries vs applications distinction makes a lot of sense. I find my programs (typically API servers) need the the equivalent of `anyhow` + `thiserror` (hence why I wrote `uni_error` - still new and experimental, and evolving).

An example of error handling with `uni_error`:

    use uni_error::*;

    fn do_something() -> SimpleResult> {
        std::fs::read("/tmp/nonexist")
            .context("Oops... I wanted this to work!")
    }

    fn main() {
        println!("{}", do_something().unwrap_err());
    }
Ref: https://crates.io/crates/uni_error

Re: Thoughts on Go vs. Rust vs. Zig

#238
post #4

For a lot of stuff what I really want is golang but with better generics and result/error/enum handling like rust.

I cautiously agree, with the caveat that while I thought I would really like Rust's error handling, it has been painful in practice. I'm sure I'm holding it wrong, but so far I have tried: * thiserror: I spend ridiculous and unpredictable amounts of time debugging macro expansions * manually implementing `Error`, `From`, etc traits: I spend ridiculous though predictable amounts of time implementing traits (maybe LLMs…

If you're willing to do what you're saying in Go, exposing the errors from anyhow would basically be the same thing. The only difference is that Rust also gives all those other options you mention. The point about other people saying not to do it doesn't really seem like it's something you need to be super concerned with; for all we know, people might tell you the same thing about Go if it had the ability for similar APIs, but it doesn't

Re: Thoughts on Go vs. Rust vs. Zig

#239

Earlier quoted context omitted.

You can just as easily add context to the first example or skip the wrapping in the second.

I don't agree. There isn't a standard convention for wrapping errors in Rust, like there is in Go with fmt.Errorf -- largely because ? is so widely-used (precisely because it is so easy to reach for). The proof is in the pudding, though. In my experience, working across Go codebases in open source and in multiple closed-source organizations, errors are nearly universally wrapped and handled appropriately. The same is…

One would still use `?` in rust regardless of adding context, so it would be strange for someone with rust experience to mention it.

As for the example you gave:

    File::create("foo.txt")?;
If one added context, it would be

    File::create("foo.txt").context("failed to create file")?;
This is using eyre or anyhow (common choices for adding free-form context).

If rolling your own error type, then

    File::create("foo.txt").map_err(|e| format!("failed to create file: {e}"))?;
would match the Go code behavior. This would not be preferred though, as using eyre or anyhow or other error context libraries build convenient error context backtraces without needing to format things oneself. Here's what the example I gave above prints if the file is a directory:

    Error: 
       0: failed to create file
       1: Is a directory (os error 21)

    Location:
       src/main.rs:7

Re: Thoughts on Go vs. Rust vs. Zig

#240
post #214
post #29

> In Rust, creating a mutable global variable is so hard that there are long forum discussions on how to do it. In Zig, you can just create one, no problem. Well, no, creating a mutable global variable is trivial in Rust, it just requires either `unsafe` or using a smart pointer that provides synchronization. That's because Rust programs are re-entrant by default, because Rust provides compile-time thread-safety. If…

> [...] is trivial in Rust [...] it just requires [...] This is a tombstone-quality statement. It's the same framing people tossed around about C++ and Perl and Haskell (also Prolog back in the day). And it's true, insofar as it goes. But languages where "trivial" things "just require" rapidly become "not so trivial" in the aggregate. And Rust has jumped that particular shark. It will never be trivial, period.

He’s talking about adding a keyword. That is all. I’d call that trivial.
Post reply on HN