It should perhaps be an error to not assign an error to a variable. Internally, Google has linters that enforce this.
Since, given this, Go is no better than a language with unchecked expressions nobody handles...
if v, err = func(); err != nil {
nil, err
}
… and then went on to use `v`. Thanks to `v`s zero value being legitimate (and not nil like a pointer would be), the program continues on as if everything is okay. In case you didn’t catch it, I forgot the `return`.Rust takes a much better approach with Result, where the return value is either `Ok(v)` or `Err(e)`, and there’s no way to access a meaningless value for the other possibility.
At least then errors as return values would be solid.
Of course now they make programmers do all the extra error wrapping thing in 1.14 to pass "richer" errors...
Having the compiler force you, as is my suggestion, would make you think even more -- or not be able not to think and skip the error check or miss it.
Go doesn't allow values to just be referenced without having some use, e.g., JavaScript's `"use strict";` hack could not be done.
In general, I have never seen a bug caused by accidentally ignoring an error. It's a theoretical concern, but not a real world problem.
Regardless, the fundamental point stands. Using tuples to return “meaningless” values alongside errors allows developers to mistakenly use those meaningless values.
Warnings are bad, specifically when the warning is unambiguous (importing a package you aren't using is always wrong, though it makes debugging frustrating at times) The idea is that warnings that don't "stop" the build generally get ignored. Build most non-trivial C++ projects and count how many warnings flow past the top of your screen for an example of what they were trying to prevent.
Theoretical problem: Someone might mutate a variable intend to be constant.
Go designers: Then put a comment saying not to do that.
Real problem: People ignore compiler warnings.
Go designers: Then eliminate warnings.
Real problem: Exceptions can happen anywhere and often go unchecked.
Go designers: Then call exceptions "panics" and encourage people not to use them.
Theoretical problem: Someone might ignore an error return value.
Go designers: Let paranoid people write linters.
Etc. etc.
> Theoretical problem: Someone might mutate a variable intend to be constant.
> Go designers: Then put a comment saying not to do that.
One can only wonder why they even bothered writing a compiler when comments can solve it all.
> Real problem: Exceptions can happen anywhere and often go unchecked.
> Go designers: Then call exceptions "panics" and encourage people not to use them.
> Theoretical problem: Someone might ignore an error return value.
> Go designers: Let paranoid people write linters.
Because that way it's even easier to ignore than exceptions, and that's… good apparently?
Also create `append` where not using the return value just right (with no help from the compiler but that's OK because comments are where it's at) doesn't just create a bug in your program it can create two or more, what relentlessly efficient focus on real-world problems.
Having to rewrite or copy/paste data structures for different types, given the lack of generics. As I understand it, even Google now has tools that generate go source from “generic” templates. This is absurd.
Defer to free resources (e.g., closing files) is a terrible construct because it requires you to remember to do this. You have lambdas! Use them so that the APIs can automatically free resources for you, like Ruby and Rust. It’s insanely hard to debug these kinds of issues because you run out of file descriptors and now you have to audit every open to ensure matching closes.
Casting to interface{}. The type system is so anemic that you have to resort to switching over this, and now you lose type safety. Combine this with the compiler not caring about exhaustive switch statements. And combine this with interfaces being implemented implicitly, and it’s a mine field for bugs.
I literally had a go maintainer waffle on adding support to calculate SSH key fingerprints because “users can just read the RFC and do it themselves if needed”. This is an indefensible perspective on software development.
Despite “making concurrency easy”, having to implement concurrency patterns by hand for your different use-cases is nuts. I have lots of feelings here, most are summed up by https://gist.github.com/kachayev/21e7fe149bc5ae0bd878
Tulpe returns feel like they were bolted on to the language. If I want to write a function that does nothing more than double the return value of a function that might error (insert your own trivial manipulation here), I have to pull out the boilerplate error handling stanza when all I want to do is pass errors up the stack.
This is the 5% that I remembered off the top of my head years later. All in all, the design of go as a “simple” language just means that my code has to be more complex.
> Obviously Go chose a different path. Go programmers believe that robust programs are composed from pieces that handle the failure cases before they handle the happy path.
A function which only returns an error can have its result ignored without any warning.