Go 언어 학습 23 오류 처리 및 실행 중 패닉(Panic)

32029 단어 go
본고는 최초로 나의 개인 블로그에 발표되어 원문을 보고 더욱 좋은 읽기 체험을 얻는다

한 가지 실수


1.1 오류 유형


약정에 따라 Go의 오류 유형은 error입니다. 이것은 내장 인터페이스입니다. nil 값은 오류가 없음을 나타냅니다.
type error interface {
        Error() string
}

오류 유형을 쉽게 사용자 정의할 수 있습니다.
package main

import (
	"fmt"
)

func main() {
	e := MyError{"This is a custom Error Type."}
	fmt.Println(e.Error())

	v1, err := divide(10, 2)
	if err == nil {
		fmt.Println(v1)
	}

	if v2, err := divide(5, 0); err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(v2)
	}
}

//  
type MyError struct {
	msg string
}

func (e *MyError) Error() string {
	return e.msg
}

//  
func divide(a1, a2 int) (int, error) {
	if a2 == 0 {
		return 0, &MyError{" , "}
	}

	return a1 / a2, nil
}

상기divide 함수는 error 값을 되돌려줍니다. 호출자는 이 오류 값에 따라 결과를 어떻게 처리하는지 판단할 수 있습니다.이런 용법은 Go에서 일종의 관용법이다. 특히 함수 라이브러리와 같은 기능을 작성할 때.예를 들어 표준 라이브러리os의 파일 열기Open 함수는 다음과 같이 정의됩니다.
// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

함수가 반환하는 구체적인 오류 유형은 PathError 입니다.
// PathError records an error and the operation and file path that caused it.
type PathError struct {
	Op   string
	Path string
	Err  error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

이 오류는 오류를 일으키는 작업과 관련 파일 경로, 오류 설명 정보를 상세하게 설명합니다.

1.2 기타 오류 유형


이외에도 표준 라이브러리에는 직간접적으로 error 인터페이스를 실현하거나 내장한 다른 미리 정의된 오류 유형이 많이 있습니다.예:
runtime.Error       //  Error 
net.Error           //  Error 
go/types.Error      //  Error 
html/template.Error //  html ( )
os/exec             //  ( )

또한 표준 라이브러리의 errors 패키지는 error 실례를 쉽게 되돌릴 수 있는 함수를 제공합니다.
// New  
func New(text string) error

구체적인 구현은 다음과 같습니다.
// errors  
package errors

// New  
func New(text string) error {
	return &errorString{text}
}

// errorString   error  ( )
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

예:
package main

import (
	"errors"
	"fmt"
)

func main() {
	fmt.Println(errors.New(" "))	
}

위의 오류 설명이 너무 간단하면 fmt 패키지의 Errorf 함수를 사용할 수 있습니다.
// Errorf , error 
func Errorf(format string, a ...interface{}) error {
	return errors.New(Sprintf(format, a...))
}

이 함수는 소프트웨어 패키지의 포맷 기능을 사용하여 설명 오류 메시지를 만들 수 있도록 합니다.
package main

import (
	"fmt"
)

func main() {
	const name, id = "bimmler", 17
	err := fmt.Errorf("user %q (id %d) not found", name, id)
	if err != nil {
		fmt.Print(err)
	}
}

일반적으로 상기 두 가지 방법은 절대 다수의 잘못된 장면을 만족시킬 수 있다.만약 여전히 부족하다면, 본문에서 말한 바와 같이, 당신은 임의의 오류 유형을 사용자 정의할 수 있습니다.

2 Panic(공황)


내장 함수panic는 실행 중 오류가 발생할 수 있으며, 이 함수를 호출하면 현재goroutine는 정상적인 실행 절차를 정지합니다.이런 상황은 일반적으로 일부 중요한 매개 변수가 부족한 검사를 할 때 발생한다. 만약에 이 매개 변수가 부족하면 프로그램이 정상적으로 운행하지 못하게 하기 때문에 프로그램을 계속 운행하게 하는 것보다 (근본적으로 정상적으로 운행할 수 없을 수도 있다) 제때에 중지하게 하는 것이 낫다.
func panic(v interface{})

이 함수는 임의의 형식의 실참 (일반적으로 문자열) 을 받아들여 프로그램이 끝날 때 인쇄합니다.
package main

func main() {
	panic(" 。")
}

다른 유형에서는 장면을 사용합니다.
package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("wait for init...")
}

var user = os.Getenv("USER")

func init() {
    //  
	if user == "" {
		panic("no value for $USER")
	}
}

일반적인 상황에서 우리는panic, 특히 라이브러리 함수에서 사용하는 것을 피해야 한다.panic가 호출된 후 (명확하지 않은 실행 중 오류, 예를 들어 수조나 절편 인덱스 월, 유형 단언 실패) 등은 현재 함수의 실행을 즉시 중지하고 goroutine 창고를 거슬러 올라가 지연된 함수를 실행합니다.goroutine 창고의 맨 위에 거슬러 올라가면 프로그램이 종료됩니다.
가령 함수F가 호출되었다고 가정하면panic의 정상적인 실행은 즉시 중단됩니다.F 중 지연된 함수는 순서대로 실행되고 F 호출처로 되돌아옵니다.호출자F에 대해서도 이 때 G 함수를 호출하는 것처럼 실행을 멈추고 panic 지연된 모든 함수를 거슬러 올라가기 시작합니다.이 G 의 모든 함수가 멈출 때까지 계속 거슬러 올라가면 프로그램이 종료되고 오류 정보를 보고합니다. goroutine 에게 전달된 매개 변수를 포함합니다.
물론 우리는 내장함수panic를 사용하여 복구하고 recover의 제어권을 되찾아 계속 아래를 볼 수 있다.
우리는 defer 문장 한 문장에서 goroutine 창고는 defer 순서로 집행된다고 언급한 적이 있다.

3 Recover(복구)


내장 함수LIFO는 발생recoverpanicking을 정상적으로 운행할 수 있다.지연된 함수에서 실행goroutine하면 recover의 발생을 중지할 수 있습니다.주의는 지연된 함수에 직접 있어야 합니다.지연 함수에서 (또는 간접적으로) 이 함수를 호출하지 않으면 아무런 작용도 일어나지 않고 panic 되돌아옵니다.프로그램이 발생하지 않았거나 nil 의 매개 변수가 panic 이면 panic 의 반환값도 nil 입니다.
func recover() interface{}

다음 예제에서는 panic과 recover의 작업 메커니즘을 보여 줍니다.
package main

import "fmt"

func main() {
	f()
	fmt.Println("  f()  。")
}

func f() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("  f()  。", r)
		}

	}()
	fmt.Println("  g()。。。")
	g(0)
	fmt.Println("  g()  。")
}

func g(i int) {
	if i > 3 {
		fmt.Println("Panicking!")
		panic(fmt.Sprintf("%v", i))
	}
	defer fmt.Println(" g() ", i)
	fmt.Println(" g() ", i)
	g(i + 1)
}

func h() {
	fmt.Println("hello")
}

effective_ 보기go의 예:
서버에서 실패 recover 를 종료하고 다른 실행 중인 nil 을 죽일 필요가 없습니다.
func server(workChan chan *Work) {
    for work := range workChan {
        go safelyDo(work)
    }
}

func safelyDo(work *Work) {
    defer func() {
        if err := recover(); err != nil {
            log.Println("work failed:", err)
        }
    }()
    do(work)
}

복구 모드를 적절하게 사용하면 goroutine 함수(및 호출된 모든 코드)는 호출goroutine을 통해 더 나쁜 결과를 피할 수 있다.우리는 이런 사상을 이용하여 복잡한 소프트웨어 중의 오류 처리를 간소화할 수 있다.do 패키지의 이상적인 버전을 보면 국부적인 오류 유형 호출 panic 으로 오류를 보고합니다.다음은 regexp 유형, panic 방법 및 Error 함수의 정의입니다.
//  Error  ,  error  。
type Error string
func (e Error) Error() string {
    return string(e)
}

// error   *Regexp  ,  Error  Panic 。
func (regexp *Regexp) error(err string) {
    panic(Error(err))
}

// Compile  。
func Compile(str string) (regexp *Regexp, err error) {
    regexp = new(Regexp)
    //  ,doParse panic
    defer func() {
        if e := recover(); e != nil {
            regexp = nil    //  
            err = e.(Error) //  , Panic。
        }
    }()
    return regexp.doParse(str), nil
}
error 트리거된 경우 Compile 복구 블록은 반환 값을 doParse 로 설정합니다. 지연된 함수는 이름이 지정된 반환 값을 수정할 수 있습니다.panic의 값을 부여하는 과정에서 우리는 그것이 국부적인 유형nil을 가지고 있는지 단언함으로써 그것을 검사할 것이다.만약 그것이 없다면, 형식 단언은 실패할 것입니다. 실행 중 오류가 발생하고, 모든 것이 중단된 적이 없는 것처럼 창고의 거슬러 올라갑니다.이 검사는 색인 경계선 넘기 같은 의외의 사고가 발생하면 우리가 errError 을 사용하여 해석 오류를 처리하더라도 코드는 실패할 수 있음을 의미한다.
적절한 오류 처리를 통해 panic 방법(구체적인 유형에 귀속되는 방법이기 때문에 내장된 recover 유형과 이름이 같아도 상관없음)은 보고 오류를 쉽게 처리할 수 있고 거슬러 올라가는 해석 창고를 수동으로 처리할 염려가 없다.
if pos == 0 {
    re.error("'*' illegal at start of expression")
}

비록 이런 모델은 매우 유용하지만, 그것은 반드시 가방 안에서만 사용해야 한다.error 내부의 error 호출을 Parse 값으로 바꾸고 호출자에게 노출되지 않습니다.이것은 준수할 만한 좋은 규칙이다.
또 이런 재촉발panic의 관용법은 실제 오류가 발생할 때error의 값을 바꾼다.그러나 원시적이든 새로운 오류든 붕괴 보고서에 나타나기 때문에 문제의 근원은 여전히 볼 수 있다.이런 간단한 재촉발 panic 모델은 이미 충분하다. 왜냐하면 그것은 단지 붕괴일 뿐이다.그러나 원본 값만 표시하고 싶다면 코드를 많이 써서 필요하지 않은 문제를 필터한 다음 원본 값으로 다시 터치할 수도 있다panic.
참조:https://golang.org/doc/effective_go.html#errors https://golang.org/pkg/builtin/#error https://blog.golang.org/defer-panic-and-recover

좋은 웹페이지 즐겨찾기