Live data from Hacker News

Twelve Go Best Practices

talks.golang.org

141–150 of 153 posts

Re: Twelve Go Best Practices

#141

if err == nil { _, err := w.Write([]byte(g.Name)) if err == nil { err := binary.Write(w, binary.LittleEndian, g.Age) if err == nil { return binary.Write(w, binary.LittleEndian, g.FurColor) } return err } return err } Why does anyone have to tell people not to do this? How does it enter anyone's mind as a thing to do in the first place? I've been known to go too far to minimize nesting. I get twitchy at the second lev…

Because the alternative is worse. If you test-and-return after every call, your function has multiple exit points and is far less maintainable. If you nest like this, you at least have a chance of maintaining a single exit point in your function (even though this example fails to do so). This is why the Lord invented exceptions, which it seems that Go does not use. This one example is enough to convince me to never u…

> If you test-and-return after every call, your function has multiple exit points and is far less maintainable.

There is nothing wrong with multiple exit points as long as:

a. the language has a mechanism for scoped resource allocation (ie. defer, finally, with, unwind-protect or "RAII")

b. the early exit doesn't happen in the middle of a long and complex function

Re: Twelve Go Best Practices

#142
post #26

Something that doesn't sit right with me is the use of a "channel of bool" when the receiving goroutine doesn't actually care whether true or false is sent. It muddies the API to force the sender to choose one of two values when all that's really wanted is an amorphous signal. e.g. in http://talks.golang.org/2013/bestpractices.slide#25 , the first case in the select will trip regardless of which value arrives, yet th…

Just sending "true" as a signal is a convention, but some people prefer something like http://play.golang.org/p/oXapo7R4RX

I'll add that the reason for using `struct{}` over `bool`, is that an empty struct occupies 0 bytes, whereas a boolean occupies 1.

Re: Twelve Go Best Practices

#143
post #23

Interesting that this snippet: func (g *Gopher) DumpBinary(w io.Writer) error { err := binary.Write(w, binary.LittleEndian, int32(len(g.Name))) if err != nil { return err } _, err = w.Write([]byte(g.Name)) if err != nil { return err } err = binary.Write(w, binary.LittleEndian, g.Age) if err != nil { return err } return binary.Write(w, binary.LittleEndian, g.FurColor) } could be written like this: func (g *Gopher) Dum…

  func must(err error) {
    if err != nil { panic(err) }
  }  

  func (g *Gopher) DumpBinary(w io.Writer) (err error) {
    defer func() {
      err, _ = recover().(error)
    }
    must(binary.Write(w, binary.LittleEndian, int32(len(g.Name))))
    must(w.Write([]byte(g.Name)))
    must(binary.Write(w, binary.LittleEndian, g.Age))
    must(binary.Write(w, binary.LittleEndian, g.FurColor))
    return
  }

Re: Twelve Go Best Practices

#144
post #19

if err == nil { _, err := w.Write([]byte(g.Name)) if err == nil { err := binary.Write(w, binary.LittleEndian, g.Age) if err == nil { return binary.Write(w, binary.LittleEndian, g.FurColor) } return err } return err } Why does anyone have to tell people not to do this? How does it enter anyone's mind as a thing to do in the first place? I've been known to go too far to minimize nesting. I get twitchy at the second lev…

I do it in C code instinctively; it's very useful for instrumentation, debugging, and resource management in straight C to have a single return point. When I get too far to the right, that's a signal that it's time to further decompose my functions; that signal is also useful, which is another thing that keeps me doing it. But that style doesn't make much sense in Golang. It makes even less sense in Ruby and Python,…

I agree, having a single return point makes debugging that much easier.

Back at Microsoft, the following pattern was used quite extensively in the OS group:

  int someFunc() {

    DWORD error = ERROR_SUCCESS; 

    error = foo();
    if (error != ERROR_SUCCESS) {
      goto Clean0;
    }

    error = bar();
    if (error != ERROR_SUCCESS) {
      goto Clean0;
    }
    
      ....

    Clean0:
    return error;
  }

Re: Twelve Go Best Practices

#145
post #23

Interesting that this snippet: func (g *Gopher) DumpBinary(w io.Writer) error { err := binary.Write(w, binary.LittleEndian, int32(len(g.Name))) if err != nil { return err } _, err = w.Write([]byte(g.Name)) if err != nil { return err } err = binary.Write(w, binary.LittleEndian, g.Age) if err != nil { return err } return binary.Write(w, binary.LittleEndian, g.FurColor) } could be written like this: func (g *Gopher) Dum…

Go supports exceptions (called "panics"). The substantive difference between Go and, e.g., Java with regard to exceptions is that Go builtin and standard library functions panic in a much narrower range of circumstances than Java's standard library. Go seems to prefer that the decision that an error condition is treated as a panic is generally left to user code that is written with more awareness of what is exception…

Go seems to prefer that the decision that an error condition is treated as a panic is generally left to user code that is written with more awareness of what is exceptional in the context of the role of that code than standard library code has.

That makes little sense in the context of ioWriters and, frankly, most contexts.

How often do you write code where you deliberately want to be oblivious of IO errors?

Re: Twelve Go Best Practices

#146
post #81
post #73

Earlier quoted context omitted.

What are the problems with checked exceptions that don't apply to go's approach?

Checked exceptions are harder to ignore :-) result, _ = someFunc()

Huh?

   try {
      result = someFunc()
   } catch {}

Re: Twelve Go Best Practices

#147
post #145

Earlier quoted context omitted.

Go supports exceptions (called "panics"). The substantive difference between Go and, e.g., Java with regard to exceptions is that Go builtin and standard library functions panic in a much narrower range of circumstances than Java's standard library. Go seems to prefer that the decision that an error condition is treated as a panic is generally left to user code that is written with more awareness of what is exception…

Go seems to prefer that the decision that an error condition is treated as a panic is generally left to user code that is written with more awareness of what is exceptional in the context of the role of that code than standard library code has. That makes little sense in the context of ioWriters and, frankly, most contexts. How often do you write code where you deliberately want to be oblivious of IO errors?

> How often do you write code where you deliberately want to be oblivious of IO errors?

The existence of error returns means that the IO library function "not panicking" and the calling code being "oblivious of IO errors" are not equivalent.

I think the motivation for the Go convention of keeping panics internal and reducing to error returns in library APIs minimizing the potential downsides of the way unchecked exceptions are not part of the declared interface of functions and yet have a major effect on control flow. A convention of using panics (which amount to unchecked exceptions) only within logically bounded units is, IMO, a sensible approach to this.

(You could do this with checked exceptions, which require additional syntax in declarations. When you already have support multiple valued returns, I don't see that checked exceptions get you much that's worth making signatures more complicated.)

Re: Twelve Go Best Practices

#148
post #144
post #19

Earlier quoted context omitted.

I do it in C code instinctively; it's very useful for instrumentation, debugging, and resource management in straight C to have a single return point. When I get too far to the right, that's a signal that it's time to further decompose my functions; that signal is also useful, which is another thing that keeps me doing it. But that style doesn't make much sense in Golang. It makes even less sense in Ruby and Python,…

I agree, having a single return point makes debugging that much easier. Back at Microsoft, the following pattern was used quite extensively in the OS group: int someFunc() { DWORD error = ERROR_SUCCESS; error = foo(); if (error != ERROR_SUCCESS) { goto Clean0; } error = bar(); if (error != ERROR_SUCCESS) { goto Clean0; } .... Clean0: return error; }

This style is used in the linux kernel as well; there can be multiple resources acquired that sometimes need to be released in reverse order, so there are often multiple labels that you can goto, depending on how much stuff you have to unwind before you return.

Re: Twelve Go Best Practices

#149
post #145

Earlier quoted context omitted.

Go seems to prefer that the decision that an error condition is treated as a panic is generally left to user code that is written with more awareness of what is exceptional in the context of the role of that code than standard library code has. That makes little sense in the context of ioWriters and, frankly, most contexts. How often do you write code where you deliberately want to be oblivious of IO errors?

> How often do you write code where you deliberately want to be oblivious of IO errors? The existence of error returns means that the IO library function "not panicking" and the calling code being "oblivious of IO errors" are not equivalent. I think the motivation for the Go convention of keeping panics internal and reducing to error returns in library APIs minimizing the potential downsides of the way unchecked exce…

When you already have support multiple valued returns, I don't see that checked exceptions get you much that's worth making signatures more complicated.

Seriously?

Does your test-suite exercise every potential I/O error and timeout on every single of your I/O calls?

Re: Twelve Go Best Practices

#150
post #126

Earlier quoted context omitted.

Naive, absolutist positions in areas of long-standing consensus between programmers of great experience and the highest imaginable competence makes one look even more ridiculous. By and large, the best programmers eschew nesting in favor of early returns. Invariably (in my experience) those who argue against early returns are inferior programmers (and not only by virtue of lacking taste in this particular debate).

Where did you get that idea of consensus? A lot of languages do not even have a return statement, neither does lambda calculus. Furthermore, CS community has long abandoned statement based languages in favor of expressions and relations which do not feature "return" for onvious reasons in forms other than equalent to jump.

I'm talking about programmers, not computer scientists. That is, people who actually accomplish things in the real world by hacking on software, rather than pontificating about it from their monadic ivory towers :) The latter have "abandoned statement-based languages," but the former absolutely have not.
Post reply on HN