Hacker News new | ask | show | jobs
by seethishat 104 days ago
The programmer is explicitly throwing away the error returned by ReadFile (using the underscore) in the criticism of Go.

    data, _ := os.ReadFile(path)
Saying that is not explicit is just wrong.
3 comments

I think the argument is that the compiler does not enforce that the error must be checked. It's just a convention. Because you know Go, you know it's convention for the second return value to be an error. But if you don't know Go, it's just an underscore.

In a language like Rust, if the return type is `Result<MyDataType, MyErrorType>`, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an error (match, if let, unwrap etc.). It literally won't compile.

When you see .unwrap in Rust code, you know it smells bad. When you see x, _ := in Go code, you know it smells bad.

> But if you don't know Go, it's just an underscore.

And if you don't know rust, .unwrap is just a getter method.

One big difference is that with unwrap in Rust, if there is an error, your program will panic. Whereas in Go if you use the data without checking the err, your program will miss the error and will use garbage data. Fail fast vs fail silently.

But I'm just explaining the argument as I understand it to the commenter who asked. I'm not saying it is right. They have tradeoffs and perhaps you prefer Go's tradeoffs.

> When you see x, _ := in Go code, you know it smells bad.

What if it’s a function that returns the coordinates of a vector and you don’t care about the y coordinate?

Haven't jumped into rust for a while. Had to read up on what .unwrap() does.

   x, _ := 
With the topic of .unwrap() _ is referencing an ignored error. Better laid out as:

  func ParseStringToBase10i32AndIDoNotCare(s string) {
     i, _ := strconv.ParseInt(s, 10, 32)
     return i
  }
Un-handled errors in Go keeps the application going were rust crashes .unwrap().

Ignoring an output data value or set is just fine. Don't always need the key and value of a map. Nor a y axes in vector<x,y,z> math.

Go has tools for checking things like this. It's just not in the compiler. If you don't want to enforce that all errors are checked, go doesn't force you to. If you do, it requires you to run an extra tool in your build process.

(Or in your commit hook. If you want to develop without worrying about such things, and then clean it up before checkin, that's a development approach that go is perfectly fine with.)

> requires you to run an extra tool

And the more I work with Go, the less I understand why warnings were not added to the compiler. Essentially instead of having them in the compiler itself, one needs to run a tool, which will have much smaller user base.

But anyway, in Go, it's sometimes fine to have both non-nil error and a result, e.g. the notorious EOF error.

> if the return type is `Result<MyDataType, MyErrorType>`, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an error (match, if let, unwrap etc.)

I think you can make the same argument here - rust provides unwrap and if you don’t know go, that’s just how you get the value out of the Result Type.

The big difference is that with `(T, error)` as a return type, any value on the caller side will look like a valid one (thanks to zero values).

  a, err := f()
  // whether you forgot to handle the `err` or not, 
  // the `a` carries a zero value, or some other value.
In rust it's not the case, as the `T` in `Result<T, E>` won't be constructed in case of an error.
> "the `a` carries a zero value, or some other value."

Or you could return pointers and use `nil` in the error case. Bonus is that it'll then panic if you try to use it without checking the error.

(Yes, I know, it makes everything else a faff and is a silly idea.)

Criticisms of Go seem like they pivot on the author's understanding of what's being done with `_` and sometimes `nil`. It's a strongly-typed language with a lot of flexibility around type, and that's nice to have when working on edge systems like a data ingester.
How about:

    file.Close()
Did you see the non handled error here? The compiler doesn’t care.