Live data from Hacker News

Thoughts on Go vs. Rust vs. Zig

sinclairtarget.com

181–190 of 599 posts

Re: Thoughts on Go vs. Rust vs. Zig

#181
post #63

> Other features common in modern languages, like tagged unions or syntactic sugar for error-handling, have not been added to Go. > It seems the Go development team has a high bar for adding features to the language. The end result is a language that forces you to write a lot of boilerplate code to implement logic that could be more succinctly expressed in another language. Being able to implement logic more succinct…

I feel like this misses the biggest advantage of Result in rust. You must do something with it. Even if you want to ignore the error with unwrap() what you're really saying is "panic on errors". But in go you can just _err and never touch it. Also while not part of std::Result you can use things like anyhow or error_context to add context before returning if theres an error.

> But in go you can just _err and never touch it.

You can do that in Rust too. This code doesn't warn:

    let _ = File::create("foo.txt");
(though if you want code that uses the File struct returned from the happy path of File::create, you can't do that without writing code that deals somehow with the possibility of the create() call failing, whether it is a panic, propagating the error upwards, or actual error handling code. Still, if you're just calling create() for side effects, ignoring the error is this easy.)

Re: Thoughts on Go vs. Rust vs. Zig

#182

Earlier quoted context omitted.

There are far more people running/writing Zig on/for systems with overcommit than not. Most of the hype around Zig come from people not in the embedded world.

If we can produce a substantial volume of software that can cope with allocation failures then the idea of using something than overcommit as the default becomes feasible. It's not a stretch to imagine that a different namespace might want different semantics e.g. to allow a container to opt out of overcommit. It is hard to justify the effort required to enable this unless it'll be useful for more than a tiny handful…

> If we can produce a substantial volume of software that can cope with allocation failures then the idea of using something than overcommit as the default becomes feasible.

Except this won't happen, because "cope with allocation failure" is not something that 99.9% of programs could even hope to do.

Let's say that you're writing a program that allocates. You allocate, and check the result. It's a failure. What do you do? Well, if you have unneeded memory lying around, like a cache, you could attempt to flush it. But I don't know about you, but I don't write programs that randomly cache things in memory manually, and almost nobody else does either. The only things I have in memory are things that are strictly needed for my program's operation. I have nothing unnecessary to evict, so I can't do anything but give up.

The reason that people don't check for allocation failure isn't because they're lazy, it's because they're pragmatic and understand that there's nothing they could reasonably do other than crash in that scenario.

Re: Thoughts on Go vs. Rust vs. Zig

#183
post #170
post #153

Earlier quoted context omitted.

But no context, so in the real world you need to write: try: f = open('foo.txt', 'w') except Exception as e: raise NecessaryContext("important information") from e Else your callers are in for a nightmare of a time trying to figure out why an exception was thrown and what to do with it. Worse, you risk leaking implementation details that the caller comes to depend on which will also make your own life miserable in th…

How is a stack trace with line numbers and a message for the exception it self not enough information for why an exception was thrown? The exceptions from something like open are always pretty clear. Like, the files not found, and here is the exact line of code and the entire call stack. what else do you want to know to debug?

It's enough information if you are happy to have a fragile API, but why would you purposefully make life difficult not only for yourself, but the developers who have their code break every time you decide to change something that should only be an internal implementation detail?

Look, if you're just writing a script that doesn't care about failure — where when something goes wrong you can exit and let the end user deal with whatever the fault was, you don't have to worry about this. But Go is quite explicitly intended to be a systems language, not a scripting language. That shit doesn't fly in systems.

While you can, of course, write systems in Python, it is intended to be a scripting language, so I understand where you are coming from thinking in terms of scripts, but it doesn't exactly fit the rest of the discussion that is about systems.

Re: Thoughts on Go vs. Rust vs. Zig

#184

> [Go] is like C in that you can fit the whole language in your head. Go isn't like C in that you can actually fit the entire language in your head. Most of us who think we have fit C in our head will still stumble on endless cases where we didn't realize X was actually UB or whatever. I wonder how much C's reputation for simplicity is an artifact of its long proximity to C++?

30 years in C/C++ here. Give an example of UB code that you have committed in real life, not from blogs. I am genuinely curious.

> Give an example of UB code that you have committed in real life

    struct foo {
        ...
        atomic_int v;
        ...
    };
    
    struct foo x;
    memset(&x, 0, sizeof(x));

Re: Thoughts on Go vs. Rust vs. Zig

#185
post #87
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…

That seems unusual. I would assume trivial means the default approach works for most cases. Perhaps mutable global variables are not a common use case. Unsafe might make it easier, but it’s not obvious and probably undesired. I don’t know Rust, but I’ve heard pockets of unsafe code in a code base can make it hard to trust in Rust’s guarantees. The compromise feels like the language didn’t actually solve anything.

> I would assume trivial means the default approach works for most cases.

I mean, it does. I'm not sure what you consider the default approach, but to me it would be to wrap the data in a Mutex struct so that any thread can access it safely. That works great for most cases.

> Perhaps mutable global variables are not a common use case.

I'm not sure how common they are in practice, though I would certainly argue that they shouldn't be common. Global mutable variables have been well known to be a common source of bugs for decades.

> Unsafe might make it easier, but it’s not obvious and probably undesired.

All rust is doing is forcing you to acknowledge the trade-offs involved. If you want safety, you need to use a synchronization mechanism to guard the data (and the language provides several). If you are ok with the risk, then use unsafe. Unsafe isn't some kind of poison that makes your program crash, and all rust programs use unsafe to some extent (because the stdlib is full of it, by necessity). The only difference between rust and C is that rust tells you right up front "hey this might bite you in the ass" and makes you acknowledge that. It doesn't make that global variable any more risky than it would've been in any other language.

Re: Thoughts on Go vs. Rust vs. Zig

#186
> In both Go and Rust, allocating an object on the heap is as easy as returning a pointer to a struct from a function.

I can't figure out what the author is envisioning here for Rust.

Maybe, they actually think if they make a pointer to some local variable and then return the pointer, that's somehow allocating heap? It isn't, that local variable was on the stack and so when you return it's gone, invalidating your pointer - but Rust is OK with the existence of invalid pointers, after all safe Rust can't dereference any pointers, and unsafe Rust declares the programmer has taken care to ensure any pointers being dereferenced are valid (which this pointer to a long dead variable is not)

[If you run a new enough Rust I believe Clippy now warns that this is a bad idea, because it's not illegal to do this, but it's almost certainly not what you actually meant]

Or maybe in their mind, Box is "a pointer to a struct" and so somehow a function call Box::new(some_goose) is "implicit" allocation, whereas the function they called in Zig to allocate memory for a Goose was explicit ?

Re: Thoughts on Go vs. Rust vs. Zig

#187

    > OOP has been out of favor for a while now
I love these lines. Who writes this stuff? I'll tell you: The same people on HN who write "In Europe, X is true." (... when Europe is 50 countries!).

    > Zig is a language for data-oriented design.
But not OOP, right? Or, OOP couldn't do the same thing?

One thing that I have found over umpteen years of reading posts online: Americans just love superlatives. They love the grand, sweeping gesture. Read their newspapers; you see it every day. A smidge more minimalism would make their writing so much more convincing.

I will take some downvotes for this ad hominem attack: Why does this guy have 387 connections on LinkedIn? That is clicking the "accept" button 387 times. Think about that.

Re: Thoughts on Go vs. Rust vs. Zig

#188

> Other features common in modern languages, like tagged unions or syntactic sugar for error-handling, have not been added to Go. > It seems the Go development team has a high bar for adding features to the language. The end result is a language that forces you to write a lot of boilerplate code to implement logic that could be more succinctly expressed in another language. Being able to implement logic more succinct…

It's just as easy to add context to errors in Rust and plenty of Go programmers just return err without adding any context. Even when Go programmers add context it's usually stringly typed garbage. It's also far easier for Go programmers to ignore errors completely. I've used both extensively and error handling is much, much better in Rust.

Re: Thoughts on Go vs. Rust vs. Zig

#189
post #177

Earlier quoted context omitted.

Sure, but you can do the next best thing, which is to control precisely when and where those allocations occur. Even if the possibility of crashing is unavoidable, there is still huge operational benefit in making it predictable. Simplest example is to allocate and pin all your resources on startup. If it crashes, it does so immediately and with a clear error message, so the solution is as straightforward as "pass bi…

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…

To be fair, you can enforce this just by filling all the allocated memory with zero, so it's possible to fail at startup.

Or, even simpler, just turn off over-commit.

But if swap comes into the mix, or just if the OS decides it needs the memory later for something critical, you can still get killed.

Re: Thoughts on Go vs. Rust vs. Zig

#190

Earlier quoted context omitted.

I pretty new to Rust and I’m wondering why global mutables are hard? At first glance you can just use static variable of a type supporting interior mutability - RefCell, Mutex, etc…

> I pretty new to Rust and I’m wondering why global mutables are hard? They're not. fn main() { unsafe { COUNTER += 1; println!("COUNTER = {}", COUNTER); } unsafe { COUNTER += 10; println!("COUNTER = {}", COUNTER); } } Global mutable variables are as easy in Rust as in any other language. Unlike other languages, Rust also provides better things that you can use instead.

People always complain about unsafe, so I prefer to just show the safe version.

  use std::sync::Mutex;

  static LIST: Mutex> = Mutex::new(Vec::new());

  fn main() -> Result> {

      LIST.lock()?.push("hello world".to_string());
      println!("{}", LIST.lock()?[0]);

      Ok(())
  }
Post reply on HN