资源管理与错误处理

Defer

调用特点

  • 确保调用在函数结束时发生

  • defer列表为后进先出,参数在defer语句时才计算

  func tryDefer() {
  	defer fmt.Println(1)
  	defer fmt.Println(2)
  	fmt.Println(3)
  	//return
  	panic("error occurred")
  	fmt.Println(4)
  }
  ---------
  3
  2
  1

常见使用defer调用场景

  • Open/Close
  • Lock/Unlock
  • PrintHeader/PrintFooter

错误处理

  • 常用错误处理
  file, err := os.OpenFile(filename,
  		os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666)
  if err != nil {
    if pathError, ok := err.(*os.PathError); !ok {
      panic(err)
    } else {
      fmt.Printf("%s, %s, %sn",
          pathError.Op,
          pathError.Path,
          pathError.Err)
    }
    return
  }
  defer file.Close()
  • 服务器统一错误处理
  type appHandler func(writer http.ResponseWriter,
  		request *http.Request) error
  
  func errWrapper(
  		handler appHandler) func(
  		http.ResponseWriter, *http.Request) {
  	return func(writer http.ResponseWriter,
  			request *http.Request) {
  		// panic
  		defer func() {
  			if r := recover(); r != nil {
  				log.Printf("Panic: %v", r)
  				http.Error(writer,
  					http.StatusText(http.StatusInternalServerError),
  					http.StatusInternalServerError)
  			}
  		}()
  
  		err := handler(writer, request)
  
  		if err != nil {
  			log.Printf("Error occurred "+
  					"handling request: %s",
  				err.Error())
  
  			// user error
  			if userErr, ok := err.(userError); ok {
  				http.Error(writer,
  					userErr.Message(),
  					http.StatusBadRequest)
  				return
  			}
  
  			// system error
  			code := http.StatusOK
  			switch {
  			case os.IsNotExist(err):
  				code = http.StatusNotFound
  			case os.IsPermission(err):
  				code = http.StatusForbidden
  			default:
  				code = http.StatusInternalServerError
  			}
  			http.Error(writer,
  				http.StatusText(code), code)
  		}
  	}
  }
  type userError interface {
  	error
  	Message() string
  }

Panic

  • 停止当前函数执行
  • 一直向上返回,执行每一层的defer
  • 如果没有遇见recover,程序退出

Recover

  • 仅在defer调用中使用

  • 获取panic的值

  • 如果无法处理,可以重新panic

  func tryRecover() {
  	defer func() {
  		r := recover()
  		if r == nil {
  			fmt.Println("Nothing to recover. " +
  				"Please try uncomment errors " +
  				"below.")
  			return
  		}
  		if err, ok := r.(error); ok {
  			fmt.Println("Error occurred:", err)
  		} else {
  			panic(fmt.Sprintf(
  				"I don't know what to do: %v", r))
  		}
  	}()
  
  	// Uncomment each block to see different panic
  	// scenarios.
  	// Normal error
  	//panic(errors.New("this is an error"))
  
  	// Division by zero
  	//b := 0
  	//a := 5 / b
  	//fmt.Println(a)
  
  	// Causes re-panic
  	//panic(123)
  }

Error vs Panic

  • 意料之中的使用error,如:文件打不开等
  • 意料之外的使用panic,如:数组越界

See also