이 글은 Effective Go를 일부 번역한 글입니다.


제어 구조

Go의 제어 구조는 C의 그것과 연관이 있지만 중요한 차이가 있다. dowhile 반복이 없고 조금 일반화된 for만 있다. switch는 더 유연하다. ifswitch에도 for처럼 초기화 문장을 넣을 수 있다. breakcontinue 문장엔 이름표를 붙여 벗어나거나 재개할 것을 지정할 수 있다. 또한 새롭게 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
}