进行错误处理,使事情变干的惯用方式是什么

问题描述:

Consider the following example program which is primitive stack implementation in Go:

package main

import "fmt"
import "errors"

const MAX_SIZE = 10

var a [10]int
var top int = -1

func main() {
    printStack()
    push(1)
    printStack()
    push(23)
    printStack()
    pop()
    push(2)
    push(24)
    push(56)
    push(87)
    push(97)
    push(47)
    push(37)
    push(31)
    push(69)
    printStack()
    push(75)
    println("Top element is", getTop())
}

func push(x int) (int, error) {
    if top >= (MAX_SIZE - 1) {
        return 0, errors.New("Error: Prevented *. Stack full")
    }
    top += 1
    a[top] = x
    return 0, nil
}

func pop() {
    top -= 1
}

func getTop() int {
    return a[top]
}

func printStack() {
    fmt.Println(top+1, "Stack: ", a, "Top", top)
}

Now, I read Error handling and Go & it seems the above way of returning multiple values is the way to handle errors in go.

But what I don't understand is that does do gophers check of errors on every statement? Coming from other languages this concept it bit hard for me to grok. So the question is

  1. What is the idiomatic way of handling errors in above problem?
  2. Is considered a bad practice if I don't check for errors? if yes, then I am supposed to check the output of push() everytime I call it?

Basically what I want to know if how would a experienced gopher do error handling in the above program?

Play URL: https://play.golang.org/p/_AnZLvY-28

[Update] Added a real-world program where I fetch data from database & output to json. http://play.golang.org/p/dDApCYprjU

Yes, the idiomatic way to produce errors is to return multiple values. The idiomatic way to handle errors is this:

val, err := myFunc()
if err != nil {
    // do something with err
}

// do something with val

At the end of the day it's a judgement call, but it's almost always good practice to handle errors. The code you're writing is also a bit unusual, you normally don't have 10 calls to the same function back-to-back, so the verbose error handling you'd have to do is more a result of the original code. For instance, you could use a loop:

for _, num := range []int{2, 24, 56, 87, 97, 47, 37, 31, 69} {
    _, err := push(num)
    if err != nil {
        panic(err)
    }
}

You have some other things that are more problematic than not handling the push errors though. One minor thing is there is no reason for push to always return 0, why not only have it return an error, instead of an int and an error? The bigger problem is that pop keeps decrementing top, and getTop just accesses a[top], so you can easily get a runtime panic if top becomes negative from having popped too much. You probably want some error handling or other safeguards in your pop and getTop functions.