Custom Error Handling
기존에 언어를 배웠던 것과는 Go언어는 다른점이 많기도 하다. Python에서의 try ~ except
와는 다르게 Error를 인터페이스로 제공하고 이를 사용하기 위해서는 Method를 통해서 구현해서 사용하거나 따로 제공되는 errorString이나 errors 패키지를 import하여 New()
함수를 통하는 방법도 있다.
처음 공부하는 부분이기도 하고, 가장 기본적으로 Error를 다루기 위해서 error 인터페이스의 메서드를 구현하고 이를 Custom해서 Error를 처리하도록 생각했다. (차후, Go를 이용해 다양한 프로젝트 아마.. Http 패키지를 써서 서버를 띄우거나, Go를 이용한 BlockChain에도 관심이 있어서 해볼 예정이다. 에러는 매번 중요하게 사용된다!!)
Go 공식사이트에서 제공하는 A tour of Go의 Exercies: Errors를 통해 Custom Error 구현 연습을 진행했다.
초기 코드는 다음과 같이 던져주는데,
package main
import (
"fmt"
)
func Sqrt(x float64) (float64, error) {
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
구현에 필요한 Sqrt()
함수는 다른 예제에서 사용된 Newton's Method를 통해 math 패키지에서 사용가능한 sqrt
와 비교를 하는 code를 가져오면 된다.
- Newton's Method(뉴턴방법)에 대해서는 위키 링크를 남깁니다.
몇번의 구현과정을 거치고 구글링의 도움을 받아 한 블로그에서 Newton's Method를 통해 구현하신분의 코드를 참조했다.(다 그대로 가져왔다고 해도 무방하다...ㅠ 그래도 이해는 했고 일단 문제는 Custom Error를 해보는 것에 초점을 뒀다.)
package main
import (
"fmt"
)
type ErrNegativeSqrt struct {
errNum float64
}
func (e *ErrNegativeSqrt) Error() string {
return fmt.Sprint("cannot Sqrt negative number : ", e.errNum)
}
func enough(term float64) bool {
if term < 0 { // absolute value
term = -term
}
if term < 0.000000000001 {
return true // enough
} else {
return false // not enough
}
}
func Sqrt(x float64) (float64, error) {
z := float64(1)
if x < 0 {
return 0, &ErrNegativeSqrt{errNum: x}
}
for {
if enough(z*z - x) {
return z, nil
}
z = z - (z*z-x)/(2*z)
}
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
// 출력
1.4142135623730951 <nil>
0 cannot Sqrt negative number : -2
정상출력과 Error 출력에 nil
과 0
이라는 출력이 거슬릴 수 있지만 일단은 패쓰! 정상적인 Error Handling이 되었는지만 확인하고 넘어가기로 했다.
구현과정은 다음과 같았다.
- errNum(float64)를 필드로 가지는 ErrNegativeSqrt의 구조체를 정의한다.
Error()
메서드를 정의하고 에러가 처리되는 구조체의 필드를 가져올 것이기 때문에 포인터를 사용했다.sqrt()
함수에서 parameter로 음수를 받게되면 for{}문(무한루프)를 들어가기 전에 Error를 return하도록 하였다. (return되는 0은 일단 의미 없는 값이지만, return에 대한 생략이 안되었다. _를 이용하면 될줄 알았는데 아니였다...)
추가적인 부분
구현 과정에서 A tour of Go에서 더 생각해볼 수 있는 문제에 대해서 Note로 던져주어 생각해보았다.
Note: A call to fmt.Sprint(e) inside the Error method will send the program into an infinite loop. You can avoid this by converting e first: fmt.Sprint(float64(e)). Why?
fmt.Sprint(e)
를 Error() 메서드에서 return을 하자 다음과 같은 일이 발생했다.
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e0320 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow
runtime stack:
runtime.throw(0x10c9045, 0xe)
/usr/local/Cellar/go/1.16/libexec/src/runtime/panic.go:1117 +0x72
- Note에서 의미한바와 같이 무한루프에 빠지면서 Stack overflow가 발생했다. 이유는?
-> code에서 구현상 `(e *ErrNegativeSqrt)로 구현하기 때문에 단순히 e를 넘겨주면 pointer 주소를 return하기 때문에 무한참조에 빠지게 된다. - Note에서 했던 방법과 다르게 구조체를 만들면서 진행을 했다. 하지만
type ErrNegativeSqrt float64
로 주면서 Error()에 Pass By Value로 구현하는 방법에 대해서는 아직 더 생각중이다.
'LANG > Golang' 카테고리의 다른 글
[Golang][Algo] Python으로 코딩하고 Go로 한번 더 풀기 -2- Trapping Rain Water (0) | 2021.03.22 |
---|---|
[Golang] 헷갈리는 Type assertion 이해하기 (0) | 2021.03.16 |
[Golang] cannot type switch on non-interface value v (type int) (0) | 2021.03.15 |