go언어
이 글은 Effective Go를 일부 번역한 글입니다.
제어 구조
Go의 제어 구조는 C의 그것과 연관이 있지만 중요한 차이가 있다. do
와 while
반복이 없고 조금 일반화된 for
만 있다. switch
는 더 유연하다. if
와 switch
에도 for
처럼 초기화 문장을 넣을 수 있다. break
와 continue
문장엔 이름표를 붙여 벗어나거나 재개할 것을 지정할 수 있다. 또한 새롭게 type switch
와 다방향 통신 멀티플렉서 select
가 제어 구조에 추가되었다. 구문도 약간 다르다. 괄호가 없으며 본문은 반드시 항상 중괄호로 감싸야 한다.
Switch
Go의 switch는 C보다 더 종합적이다. 식은 상수가 아니어도 되고, 심지어 정수가 아니어도 된다. 각 경우는 위에서 아래로 일치하는 경우가 있을 때까지 검사한다. 그리고 switch에 식이 없다면 true로 대신한다. 이런 특성 덕에 switch로 if-else-if-else 사슬을 작성하는 게 가능하며, 또 자연스럽다.
func unhex(c byte) byte { switch { case '0' <= c && c <= '9': return c - '0' case 'a' <= c && c <= 'f': return c - 'a' + 10 case 'A' <= c && c <= 'F': return c - 'A' + 10 } return 0 }
아래로 자동으로 흐르지 않지만, 여러 경우를 반점으로 구분해 나열할 수 있다.
func shouldEscape(c byte) bool { switch c { case ' ', '?', '&', '=', '#', '+', '%': return true } return false }
C와 비슷한 다른 언어에서 일반적인 것과는 다르게 Go에서 일반적인 건 아니지만, break로 switch를 미리 끝낼 수 있다. 하지만 가끔 switch가 아니라 switch를 감싸는 반복을 끝낼 필요가 있고, Go에선 반복에 이름표를 붙여 이름표를 종료할 수 있다. 아래 예제는 두 경우 모두를 보여준다.
Loop: for n := 0; n < len(src); n += size { switch { case src[n] < sizeOne: if validateOnly { break } size = 1 update(src[n]) case src[n] < sizeTwo: if n+1 >= len(src) { err = errShortInput break Loop } if validateOnly { break } size = 2 update(src[n] + src[n+1]<< shift) } }
물론, continue도 이름표를 사용할 수 있지만, 오직 반복에만 적용할 수 있다.
switch 두 개로 바이트 조각을 비교하는 루틴으로 이 절을 마무리한다.
// Compare returns an integer comparing the two byte slices, // lexicographically. // The result will be 0 if a == b, -1 if a < b, and +1 if a > b func Compare(a, b []byte) int { for i := 0; i < len(a) && i < len(b); i++ { switch { case a[i] > b[i]: return 1 case a[i] < b[i]: return -1 } } switch { case len(a) > len(b): return 1 case len(a) < len(b): return -1 } return 0 }
Type Switch
switch를 사용해 인터페이스 변수의 동적인 형을 발견할 수도 있다. 이를 type switch라 부르며, 형 주장(Type Assertion) 구문 괄호 안에 type을 넣어 사용한다. switch 식에 변수를 선언하면 변수는 각 절마다 그에 상응하는 형이 될 것이다. 사실상 같은 이름으로 변수를 선언해도 각 경우마다 형이 다르다면 이름을 다시 사용하는 게 또 자연스럽다.
var t interface{} t = functionOfSomeType() switch t := t.(type) { default: fmt.Printf("unexpected type %T\n", t) // %T prints whatever type t has case bool: fmt.Printf("boolean %t\n", t) // t has type bool case int: fmt.Printf("integer %d\n", t) // t has type int case *bool: fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool case *int: fmt.Printf("pointer to integer %d\n", *t) // t has type *int }