상수에 대한 오해

C/C++, Java 개발자들의 상수에 대한 오해 풀기

상수에 대해서만 벌써 네 번째 글이다.

그만큼 Go의 상수는 C/C++이나 Java를 사용했던 개발자가 오해할만한 요소를 많이 포함하고 있다.

우선 C/C++에서 상수를 만드는 키워드인 constfinal에 대해 간략히 정리해보자.


C/C++ const의 역할

  • 변수의 읽기전용 설정 = 상수화
  • (C++) 멤버 함수의 멤버 변수 변경 금지

Java final의 역할

  • 변수의 읽기전용 설정 = 상수화
  • 메서드의 오버라이딩 금지
  • 클래스의 상속 금지

다른 것들은 같은 키워드를 사용할 뿐 상수라 부르지 않고, 사실 Go에는 없는 기능들이니 첫 번째 역할에 대해서만 살펴보자.

Go와의 확실한 구분을 위해 일부러 변수의 읽기전용화라 표현했다. C/C++, Java에서 상수는 변수를 만드는 것과 동일하게 이름과 타입을 정한 후 최초 한 번 값을 설정하면 읽기전용이 돼 변경이 불가능하다.

const int i = 10;
final int i = 10;

변수를 읽기전용화한 것이기 때문에 다음과 같은 문법은 당연하며 자연스럽다.

// c
struct MyStruct
{
	int i;
};

const struct MyStruct my = {0};

변수를 만들 수 있으면 상수도 만들 수 있다.

이 당연했던 사실이 Go에서는 오해의 요인이 된다.

변수는 변수, 상수는 상수

위의 C 예처럼 Go에서 구조체를 상수로 만들어 보자.

package main

import "fmt"

type MyStruct struct {
	i int
}

func main() {
	const my MyStruct = MyStruct{10}
	fmt.Println(my.i)
}

이 코드는 다음과 같은 오류가 발생한다.

./main.go:10:8: const initializer MyStruct literal is not a constant

이와 같이 Go에서는 타입이라고 해서, 즉 변수를 만들 수 있다고 해서, 상수를 만들 수 있는 것은 아니다. Go가 지원하는 상수의 종류는 boolean, rune, integer, floating-point, complex, string 등 6가지 밖에 없다. Go spec.의 관련 내용은 다음과 같다.

There are boolean constantsrune constantsinteger constantsfloating-point constantscomplex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.

정수형 상수의 내부 처리에서 확인한 것처럼 정수형 상수라고 해서 그 타입도 int가 되는 것은 아니다. 즉, Go에서 상수는 읽기전용 변수가 아니라 그냥 상수인 것이다.

Go spec.에 상수에 대한 구현 요구사항이 있으니 마지막으로 확인하고 마무리하자.

Implementation restriction: Although numeric constants have arbitrary precision in the language, a compiler may implement them using an internal representation with limited precision. That said, every implementation must:

  • Represent integer constants with at least 256 bits.
  • Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed binary exponent of at least 16 bits.
  • Give an error if unable to represent an integer constant precisely.
  • Give an error if unable to represent a floating-point or complex constant due to overflow.
  • Round to the nearest representable constant if unable to represent a floating-point or complex constant due to limits on precision.

These requirements apply both to literal constants and to the result of evaluating constant expressions.