봉인해제 switch

switch의 변신은 무죄

Go의 switch 작성 규칙은 다음과 같다.

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .

이 중 ExprSwitchStmt 규칙은 다음과 같다.

ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .

자세히 보면 switch 뒤에도, case 뒤에도 단순히 Expression이 올 수 있다. C/C++, Java에 익숙한 개발자라면 단서를 기대할 것이다.

C의 switch를 보자.

switch (expression)
{
	case constant_expression:
		//statement
		break;
	default:
		//statement
}
  • The expression used in a switch statement must have an integral or enumerated type, or be of a class type in which the class has a single conversion function to an integral or enumerated type.
  • The constant-expression for a case must be the same data type as the variable in the switch, and it must be a constant or a literal.

Java는 다음과 같다.

switch (expression) {
	case value:
		//statement
		break;
	default:
		//statement
}
  • The expression used in a switch statement can only be integers, convertable integers (byte, short, char), strings and enums.
  • The value for a case must be the same data type as the variable in the switch and it must be a constant or a literal.

JDK7 이전의 옛날 얘기는 하지 말고, Java는 문자열도 switch에 쓸 수 있다는 점을 제외하면 그놈이 그놈이다. C가 버전 1.0이라면 Java는 버전 1.1 정도라고나 할까.

Go switch 버전을 몇으로 해야 할지는 이견이 있겠으나 최소한 1.2, 1.3 수준이 아닌 것은 확실하다. 여기에 이 글에서는 살펴보지 않은 TypeSwitchStmt 용법까지 포함하면 Major 버전을 몇 단계는 올려야 한다. 그래서 봉인해제다.

타입 조건 해제

Go는 숫자형이 아니어도, 문자열이 아니어도 switch에 사용할 수 있다. 안타깝게도 아직 다양한 타입에 대해서는 다루지 않았으니 여기서는 그냥 넘어가도록 하자. 사실 다양한 타입을 쓸 수 있다는 것은 그리 특별한 것은 아니다.

상수 조건 해제

가장 눈에 띄는 것은 case 뒤에 오는 Expression이 상수일 필요가 없다는 점일 것이다. 즉, 다음과 같은 코드도 가능하다.

var c = 3

var c1, c2 = 1, 2

switch c {
case c1:
	fmt.Println("c1")
case c2:
	fmt.Println("c2")
case c1 + c2:
	fmt.Println("c1 + c2")
}

case 뒤 Expression이 상수일 필요가 없다는 사실은 예상보다 많은 코드 다양성을 보장한다. 예를 들어, C나 Java로 testFunc()의 반환값이 1일 때와 그렇지 않을 때 다른 문자열을 출력하는 코드를 switch로 작성한다고 가정해 보자.1

// c code
switch (testFunc())
{
	case 1:
		printf("1\n");
	default:
		printf("default\n")
}

그런데 Go는 하늘과 땅을 뒤집을 수도 있다.

// go code
switch 1 {
case testFunc():
	fmt.Println("1")
default:
	fmt.Println("default")
}

Go는 switch 뒤 Expression을 생략할 수도 있다. 생략한 경우 true가 있는 것으로 가정한다. 즉, 다음 두 switch 코드는 같은 의미이다.

switch { /* */ }

switch true { /* */ }

switch 뒤 Expression이 true이니 case 뒤 Expression은 참인 코드가 오면 된다.

switch {
case 1 > 2:
	// statement    
case 1 = 2:
	// statement
case 1 < 2:
	// statement
}

  1. 물론 이런 코드를 switch로 작성하지는 않는다. ↩︎