Live data from Hacker News

Go Style

google.github.io

141–150 of 212 posts

Re: Go Style

#141

> The general rule of thumb is that the length of a name should be proportional to the size of its scope and inversely proportional to the number of times that it is used within that scope. > A variable created at file scope may require multiple words, whereas a variable scoped to a single inner block may be a single word or even just a character or two, to keep the code clear and avoid extraneous information. I love…

This advice should be universal in coding. When I first started programming, I had a manager that hated 1-2 character variables. But they make sense for loop iterators.

When I started coding I'd type

   10 FOR F = 1 TO 10 
   20   PRINT F, F*F
   30 NEXT F
Because "FOR" and "F" shared the same key, on my ZX Spectrum keyboard.

Re: Go Style

#142
post #63

Earlier quoted context omitted.

The constancy in Go makes this better. I have come to expect `r` to be an io.Reader or http.Request depending on context. There are a few interfaces in Go that are used heavily and I don't mind that people often use a single character for them. It's the same thing as everyone using `i` for iterators.

I just use reader//req. Code is instantly easier to read for me personally and no time has been wasted. It's all preference, of course.

This is much nicer for other people coming into this. It's the same problem with spoken language and slang, slang is better if you know it, worse if you don't.

Re: Go Style

#143

Earlier quoted context omitted.

And why isn't this build-in since the beginning like in any other sane language? A language where you need to constantly write wrappers around everything just to get basic functionality is quite a miserable language, imho. That's like C…

> A language where you need to constantly write wrappers around everything just to get basic functionality is quite a miserable language, imho. That's like C… Which makes complete sense, since Go was designed as Google's C for Dummies.

I know. Even by the same people that like to repeat their mistakes.

Re: Go Style

#144

Earlier quoted context omitted.

> Somewhere, else you risk not knowing that a change to ConcreteType broke is implementation of IfaceType in a way that you might not know about until a rare runtime code path is executed. Why would runtime be involved? Surely if ConcreteType doesn't satisfy the interface anymore then the compiler will catch that at any site where a ConcreteType is being used as / cast to an IfaceType? Or are you talking about iface-…

The compiler will catch it at the point of use, but it's clearer if the error is at the point of declaration. Consider: type FooReader struct{} var _ io.Reader = FooReader{} func (r FooReader) Raed(p []byte) (int, error) { return 0, nil } Before you even use FooReader as an io.Reader, you can find that you typo'd "Read". This is useful in practice because you probably wrote FooReader to have a bunch of other methods…

Hah, how’s that for an endorsement of structurally typed interfaces.

Re: Go Style

#145

In my opinion the hardest style rules to accept when trying to use this guide are: 1. Do not create "assertion libraries" like `assertEqual(x, y)` [1] 2. Leave testing to the Test function [2] 3. Intialisms (HTTPURL, IOS, gRPC) [3] 4. Function formatting [4] For the record I'm not saying I disagree with these. I just think that folks coming from other languages have a lot of built in muscle memory to do it other ways…

I've read the assertions section a few times and I still don't understand the argument. How is: if got == nil { t.Errorf("blog post was nil, want not-nil") } Better than assert.NotNil(t, got, "blog post") ? They seem to suggest that you lose context, but their "Good" examples are similarly devoid of context.

The main reason I dislike the assert libraries is that you lose the ability to format things nicely; being able to see what's going on quickly is pretty nice when tests fail. Sometimes you want %s, sometimes %q, sometimes maybe a diff or formatted as something else. Testify "solves" this by just dumping a lot of stuff to your terminal, which is a crude brute-force solution. Plus with table-driven test you're saving what, 2 or 3 lines of code?

For example for a simple string comparison it's 12 lines:

   --- FAIL: TestA (0.00s)
       --- FAIL: TestA/some_message (0.00s)
           main_test.go:11:
                   Error Trace:    /tmp/go/main_test.go:11
                   Error:          Not equal:
                                   expected: "as\"d"
                                   actual  : "a\"sf"
   
                                   Diff:
                                   --- Expected
                                   +++ Actual
                                   @@ -1 +1 @@
                                   -as"d
                                   +a"sf
                   Test:           TestA/some_message
 
vs. 2 lines with a "normal" test:

   --- FAIL: TestA (0.00s)
       --- FAIL: TestA/some_message (0.00s)
           main_test.go:11:
               have: "as\"d"
               want: "a\"sf"
With your NotNil() example it's 4 lines, which seems about 3 lines more than needed.

This kind of stuff really adds up if you have maybe 3 or 4 test failures.

Re: Go Style

#146

In my opinion the hardest style rules to accept when trying to use this guide are: 1. Do not create "assertion libraries" like `assertEqual(x, y)` [1] 2. Leave testing to the Test function [2] 3. Intialisms (HTTPURL, IOS, gRPC) [3] 4. Function formatting [4] For the record I'm not saying I disagree with these. I just think that folks coming from other languages have a lot of built in muscle memory to do it other ways…

I've read the assertions section a few times and I still don't understand the argument. How is: if got == nil { t.Errorf("blog post was nil, want not-nil") } Better than assert.NotNil(t, got, "blog post") ? They seem to suggest that you lose context, but their "Good" examples are similarly devoid of context.

NotNil is fine, but as a library author you need to implement every comparison operator for every type. You also have to implement each assertion twice; once for t.Error and once for t.Fatal. We have a homegrown assertion library in our codebase at work, and every time I write a test I have to settle for an inaccurate assertion, or add two more methods to the library. I grew up on Go at Google where I would not have been allowed to check in assertion helpers, and I think they made the right call there.

I've seen a lot of gore in code that uses assertion libraries like assert.Equals(t, int64(math.Round(got)), int64(42)). Consider the error message in that case when got is NaN or Inf.

It is so easy to write your own assertions. I can type them in my sleep and in seconds:

    if got, want := f(), 42; got != want { 
        t.Errorf("f:\n  got: %v\n want: %v", got, want)
    }

    if got, want := g(), 42.123; math.Abs(got - want) > 0.0001 {
        t.Fatalf("g:\n  got: %v\n want: %v", got, want)
    }
Why implement a NotEquals function when != is built into the language?

(The most popular assertion library also inverts the order of got and want, breaking the stylistic convention. It's so bad. Beware of libraries from people that wrote them as their first Go project after switching to Go from Java. There are a lot of them out there, and they are popular, and they are bad.)

Finally, cmp.Diff is the way to go for complex comparisons. Most use of assertion libraries can be replaced by cmp.Diff. I wouldn't use it for simple primitive type equality, but for complex data structures, it's great. And very configurable, so you never have to "settle" for too much strictness or looseness.

Re: Go Style

#147

Go is absurd. It's opinionated in all the wrong ways. > Functions that return something are given noun-like names. > // Good: > func (c Config) JobName(key string) (value string, ok bool) > A corollary of this is that function and method names should avoid the prefix Get. > // Bad: > func (c Config) GetJobName(key string) (value string, ok bool) That's dumb. I'd like a function to be GetJobName to indicate that it do…

Just assume that "Noun()" gets the noun and it's effectively the same as "GetNoun()". Of course, nothing in the language prevents you from using "GetNoun()" if you strongly prefer that.

> The other day, I spent a whole day trying to figure out the "idiomatic" way to return a an object not found case from my db. Do you return a nil pointer (don't, passing pointers leads to bugs), or an empty struct (then how do you reliably "test" its emptiness?) or an error?

sql.ErrNoRows is often used for this.

Re: Go Style

#148

The kubernetes ecosystem has a lot of go code which consequentially suffer from nil panics. Thankfully they recover otherwise we'd see an absolute shit ton of pod restarts. In general, please stop panicking in library Go and Rust code. It's rude.

nil panics are hard bugs, not stylistic concerns. There is never an occasion where correct code dereferences a nil pointer.

The style point that people are missing here is that every type should have a usable zero value. If (&SomeStruct{}).Method() panics, that's an API design flaw.

Re: Go Style

#149
post #2

Isn’t the point of go is that go fmt follows the languages global style guide?

At first glance, that question makes sense; however, fmt doesn't cover all code format issues. Ex from the post: > Line length > There is no fixed line length for Go source code. If a line feels too long, it should be refactored instead of broken. If it is already as short as it is practical for it to be, the line should be allowed to remain long. > Do not split a line: > Before an indentation change (e.g., function…

I hate autoformatters/linters that frob with line length with a passion. I'm an 80-column kind of guy, but 81 columns can be just fine, and in some cases 90 or even 100 can be more readable than obsessively wrapping at 80 (or some other arbitrary value).

There are a few (rare) cases where gofmt will wrap lines, but it mostly leaves it alone which is one of the better "features" IMHO. Many *fmt tools really got this wrong.

Automated formatters are nice, but in the end there is no substitute for human eyes and common sense.

Re: Go Style

#150

golang driving me nuts these days. enjoying the performance, but time.Parse put me into a ragequit mode last night and I really wish it was possible to return a thing OR nil

Why not writing a small wrapper around time.Parse? func ParsedTimeOrNil(s string) *time.Time { t1, err := time.Parse(time.RFC3339, s) if err != nil { return nil } return &t1 }

Hiding errors like that is going to make it hard to figure out what is going on as more and more wrappers like this pile up.

If time.Parse is failing, you either have bad input or a bug, right? If you are ok with this failing without even logging the error, something might be wrong with the design.

Post reply on HN