Live data from Hacker News

An Honest Review of Go (2025)

benraz.dev

151–160 of 184 posts

Re: An Honest Review of Go (2025)

#151

Earlier quoted context omitted.

Fair take for nontrivial projects. Buuut with Go one in general tends to reach less for dependencies so less likely to run into this and cgo is not go ;) https://go-proverbs.github.io but for cross-compiling actually ended up filtering out the liconv flag with a bash wrapper and compiled a custom zig cc version with the support for exported_symbols_list patched in, things appear to work. Should look into cross-rs I s…

Cross compiling to Apple products from non Apple products is going to run into the same hurdle around SDK setup as any other. There exists documentation but it’s probably not the easiest task. This limitation though applies equally to any library that depends on system C headers and/or system libraries.

I feel like we're talking in loops.

Go is generally fine for crosscompiling.

edit: what gave me pain with Rust for a cli was clap (with derive, the default). Go just worked.

Re: An Honest Review of Go (2025)

#152

I'd encourage the author to spend more time learning Go. They've come to incorrect conclusions -- especially regarding errors. Read more of the stdlib to see how powerful they can be, e.g. net.OpError: https://cs.opensource.google/go/go/+/refs/tags/go1.25.5:src/... > The user now has an interface value error that the only thing > they can do is access the string representation of ... The only > resort the consumer of…

> e.g. net.OpError

I have my own complain against Rust's error handlings, but I can't bring myself to praise what Go has been doing.

One problem with the error interface is that you can't know exactly which error type will be returned, and the Go authors may add new error types form time to time. `net.OpError` itself is a great example for that, which is a newer type than net.Error.

This style of error signalling is best used when the error handling is binary, either "No error, continue" or "Errored out, abort", but not branched out path like "If encountered error A, do this. If encountered error B, do that. Otherwise, abort".

Rust's error handling encourages such branched out handlings by default, but if you want to so the same in Go, there will be a nightmarish manual type discovery and tracking operation waiting for you.

So for me, I rather use a dedicated signal code to tell which error branch I should do next rather than relying on the error, i.e:

    if next, err := func(); err != nil { // If errored, abort it
        return err
    } else if next == condition1 {
        return op1()
    } else if next == condition2 {
        return op2()
    } else {
        panic("unsupported condition")
    }

Re: An Honest Review of Go (2025)

#153
post #6

Earlier quoted context omitted.

exactly wtf is up with this website, firefox doesn't show t's, random f's, d's - it's a complete mess.

My best guess is that the font I am using (WOFF2 variable font) might be tripping up the browser.

That's probably it. I am getting these problems with Safari 17.6, with a newer one, Safari 26.0.1, there is no problem.

Re: An Honest Review of Go (2025)

#154
post #20

His example criticizing errors in `rootInfo` func is silly. There is utterly no need to do a `strings.HasSuffix(err.Error(), "not a directory")`.

but how would you do that otherwise? Genuinely curious cause I looked up both the go docs and source (disclaimer: not a go dev), and there doesn't seem a way to handle that specific kind of error through stuff like `errors.Is`, at least from what I can tell, at least in the os and fs packages

[deleted]

Re: An Honest Review of Go (2025)

#155
post #20

His example criticizing errors in `rootInfo` func is silly. There is utterly no need to do a `strings.HasSuffix(err.Error(), "not a directory")`.

but how would you do that otherwise? Genuinely curious cause I looked up both the go docs and source (disclaimer: not a go dev), and there doesn't seem a way to handle that specific kind of error through stuff like `errors.Is`, at least from what I can tell, at least in the os and fs packages

He can do a test using `errors.Is(err, syscall.ENOTDIR)`

  func rootInfo(root, p string) (has bool, isDir bool, err error) {
   p = path.Clean(p)
   info, err := os.Stat(root + "/" + p)
   if info != nil {
    has, isDir = true, info.IsDir()
    return
   }
   if errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) {
    err = nil
   }
   return
  }

Re: An Honest Review of Go (2025)

#156
post #152

I'd encourage the author to spend more time learning Go. They've come to incorrect conclusions -- especially regarding errors. Read more of the stdlib to see how powerful they can be, e.g. net.OpError: https://cs.opensource.google/go/go/+/refs/tags/go1.25.5:src/... > The user now has an interface value error that the only thing > they can do is access the string representation of ... The only > resort the consumer of…

> e.g. net.OpError I have my own complain against Rust's error handlings, but I can't bring myself to praise what Go has been doing. One problem with the error interface is that you can't know exactly which error type will be returned, and the Go authors may add new error types form time to time. `net.OpError` itself is a great example for that, which is a newer type than net.Error. This style of error signalling is…

  > This style of error signalling is best used when the error handling is binary,
  > either "No error, continue" or "Errored out, abort", but not branched
  > out path like "If encountered error A, do this. If encountered error B,
  > do that. Otherwise, abort".
If I'm understanding you correctly, you can implement your example in a fully idiomatic way, and the stdlib does this in a bunch of places.

Errors are just values, so you can return whatever best suits the use-case. The gamut of expected errors might not be part of type signature, but it can be part of your documentation.

The "signal code" you refer to could be implemented via:

1. Returning error structs (e.g. ErrFoo, ErrBar) and using `errors.As()` access those structs -- useful if you have a highly structured error with additional fields.

  type ErrFoo struct { // fields // }
  type ErrBar struct { // fields // }

  if var errFoo *ErrFoo; errors.As(err, &errFoo) {
    // handle ErrFoo
  }
  if var errBar *ErrBar; errors.As(err, &errBar) {
    // handle ErrBar
  }
  
2. Returning a single error struct (MyErr) with an `Op` field, as `net.OpError` does. Similarly accessing via `errors.As()`

  // top-level, e.g. exported by package
  type MyErrorStruct struct {
    Op string
    Path string
  }
  var (
    OpFoo string = "foo"
    OpBar string = "bar"
  )
  
  
  var opErr *MyErrorStruct
  if errors.As(err, &opErr) {
    if opErr.Op = OpFoo {
      // handle OpFoo at path opErr.Path
    }
    if opErr.Op = OpBar {
      // handle OpBar at path opErr.Path
    }
  }

3. Returning predefined errors, e.g. `var ErrFoo = errors.New("foo")` and checking for them via `errors.Is()`

  // top-level, e.g. exported by package
  var ErrFoo = errors.New("foo")
  var ErrBar = errors.New("bar")

  if errors.Is(err, ErrFoo) {
    // handle ErrFoo
  }
  if errors.Is(err, ErrBar) {
    // handle ErrBar
  }

Sidenote: I feel like people struggle to internalise that Go errors are just values* and are therefore are wildly flexible, yet not special in any way.

I wonder if this is because other languages treat errors as something special?

Re: An Honest Review of Go (2025)

#157
post #107

Earlier quoted context omitted.

Doesn't protection from reassignment not exist in most languages anyways? In C++ you should be able to cast away the const. Realistically you probably can achieve this in any language with reflection. Unless a const is literally a compile time constant inserted through the program, it's likely able to be changed somehow in most languages.

You can definitely protect from reassignment in those languages (e.g. `final` in Java) but they don't completely prevent you from changing the underlying data. Rust would be one that comes to mind that has true immutability. I guess the Go maintainers just didn't want to go down that road, which I get.

Sorry, I guess I read "protect from reassignment" as "protect the underlying data" then.

I would argue that if you CAN change the underlying data, then the understanding of const by 99% of people is made incorrect. Therefore it's not really a good feature (in my opinion).

Re: An Honest Review of Go (2025)

#158

I'd encourage the author to spend more time learning Go. They've come to incorrect conclusions -- especially regarding errors. Read more of the stdlib to see how powerful they can be, e.g. net.OpError: https://cs.opensource.google/go/go/+/refs/tags/go1.25.5:src/... > The user now has an interface value error that the only thing > they can do is access the string representation of ... The only > resort the consumer of…

The author is clearly aware of `error.Is` as they use it in the snippet they complain about. The problem is Go's errors are not exhaustive, the equivalent to ENOTDIR does not exist. So you can't `errors.Is` it. And while Stat does tell you what specific error type it'll be in the documentation, that error type also doesn't have the error code. Just more strings! Is this a problem with Go the language or Go the standa…

> the equivalent to ENOTDIR does not exist

https://pkg.go.dev/syscall#ENOTDIR

Re: An Honest Review of Go (2025)

#159
post #152

Earlier quoted context omitted.

> e.g. net.OpError I have my own complain against Rust's error handlings, but I can't bring myself to praise what Go has been doing. One problem with the error interface is that you can't know exactly which error type will be returned, and the Go authors may add new error types form time to time. `net.OpError` itself is a great example for that, which is a newer type than net.Error. This style of error signalling is…

> This style of error signalling is best used when the error handling is binary, > either "No error, continue" or "Errored out, abort", but not branched > out path like "If encountered error A, do this. If encountered error B, > do that. Otherwise, abort". If I'm understanding you correctly, you can implement your example in a fully idiomatic way, and the stdlib does this in a bunch of places. Errors are just values,…

> Sidenote: I feel like people struggle to internalise that Go errors are just values* and are therefore are wildly flexible, yet not special in any way.

I think it's more they haven't used Go interfaces much.

I think most programming languages these days have errors-are-values, e.g. Rust Result transports a normal value, Python & Javascript exceptions are just values even when they are carried over a wildly different control flow.

(The argument for sum types and exhaustiveness is valid, but most languages don't have those so focusing that critique on Go is misguided. They are nice in Rust.)

Re: An Honest Review of Go (2025)

#160

Earlier quoted context omitted.

Oh Go with Rust's result/none setup and maybe better consts like in the article would be great. Too ba null/nil is to stay since no Go 2. Or maybe they would? Iirc 1.21 had technically a breaking change related to for loops. If it was just possible to have migration tooling. I guess too large of a change.

Technically, with generics, you could get a Result that is almost as good as Rust, but it is unidiomatic and awkward to write: type Result[T, E any] struct { Val T Err E IsErr bool } type Payload string type ProgError struct { Prog string Code int Reason string } func DoStuff(x int) Result[Payload, ProgError] { if x > 8 { return Result[Payload, ProgError]{Err: ProgError{Prog: "ls", code: 1, "no directory"}} } return…

Nullability is unavoidable in Go because of zero values.

https://go.dev/ref/spec#The_zero_value

Post reply on HN