Go를 찍먹 하는 사람들 중에 입문자는 거의 없을 것이다.
이 글을 보는 사람들이 다른 언어를 몇가지 다뤄본 경험이 있을 것이라고 가정하고, 짧게 핵심만 정리하겠다.
우선 Go의 Primitive Type은 아래 링크를 통해 빠르게 확인할 수 있을 것이다.
다른 언어를 다뤄보았다면 1분이면 파악이 가능할 것이므로 충분히 숙지하고 글을 읽어보도록 하자.
https://go.dev/tour/basics/11
Go에서는 Basic Types라고 부르는듯 하다.
Instance는 Value라고 부르는 등, 다른 언어와 용어가 다른 부분이 존재하지만 본질은 같으므로 용어의 차이가 있다는 것만 알아두자.
package main
import (
"fmt"
)
func main() {
var i32 int
var u32 uint
var b bool
var s string
fmt.Printf("%v %v %v %v", i32, u32, b, s)
}
var <variable name> <type>
형태로 변수 선언이 이루어진 것을 볼 수 있다.
var
은 mutable variable을 선언할 때 사용하는 keyword다.
C언어와 비슷한 Printf
함수가 존재하는데, Go는 package 단위로 구성되므로 앞에 fmt라는 package 이름이 붙는다.
%v
는 해당 타입에 따라 출력 형식을 자동으로 지정해주는 format string이다.
(정수는 %d
, 문자열은 %s
, 타입은 %T
, 메모리 주소는 %p
등)
더 많은 예시는 아래 링크 참조.
https://go.dev/play/p/Kh5NwaS_89C
위 코드에서 변수를 선언했을 뿐, 따로 초기화는 하지 않은 상태로 모든 변수를 출력했다.
이제 출력값을 확인해보자
0 0 false
Go는 var
로 선언된 변수가 따로 초기화되지 않을 경우 비트 패턴이 0인 값으로 자동으로 초기화해준다.(s
만 출력되지 않은 이유는 빈 문자열이기 떄문)
이를 Zero Value라고 부른다.
말 그대로 비트 0으로 해당 타입의 size만큼 전부 채워준다고 생각하면 상당히 직관적이다.
i32 := 10
str := "string"
위와 같이 var
없이 :=
로 변수를 선언하는 방법이다.
선언과 동시에 직접 값을 초기화 하는 경우에 사용하는 보다 간략한 초기화 방법이다.
알아서 타입 추론을 해주기 때문에 값만 넣으면 된다.
전역 변수를 선언하거나 초기값 없이 변수를 선언하는 경우에는 var
을 사용하며, 타입을 직접 지정하고 싶은 경우에도 var a int64
와 같은 타입 지정이 필요하다.
타입을 지정하지 않는 경우, 정수 리터럴은 기본적으로 int
가, 실수 리터럴은 float64
가 되기 때문이다.
그 외의 경우에는 대부분 :=
를 사용한다.
Short Assignment Operator 등의 이름이 있지 않을까 했는데 일단 Go의 공식 스펙에는 Short variable declarations라고만 적혀있다.
추가) 공식 이름은
Short Assignment Statement
라고 한다.
package main
import (
"fmt"
)
func main() {
i32 := 1
u32 := 1
b := true
s := ""
fmt.Printf("%v %v %v %v", i32, u32, b, s) // 1 1 true
}
마찬가지로 빈 문자열이라 3개만 출력되는 것이다.
단, 이 방법으로 변수 섀도잉을 시도하면 아래의 오류가 발생할 것이다.
package main
import (
"fmt"
)
func main() {
a := 1
a := 2 // `no new variables on left side of :=`
}
물론 재할당은 다른 언어와 동일하게 =
로 가능하다.
package main
import (
"fmt"
)
func main() {
a := 1
fmt.Printf("[%p]: %d\n", &a, a)
// [0xc0000b2000]: 1
a = 2
fmt.Printf("[%p]: %d\n", &a, a)
// [0xc0000b2000]: 2
}
Primitive 변수 재할당 시 Java, JavaScript 등의 언어처럼 기존 메모리 공간을 버리고 새로운 메모리 공간을 사용하는지 궁금해서 찍어보니, 주소값이 변하지 않고 그대로인 것을 확인했다.
한가지 주의할 점은, :=
는 반드시 함수 내부에서만 사용 가능하며 전역 변수로는 못 쓴다는 것이다.
const
도 존재하는데 당연히 컴파일 타임에 Literal 형태로 존재하는 Sized Value만 가능할 것이다.
package main
import (
"fmt"
)
func main() {
const a = 1
const b int // missing constant value
}
var
와 달리 반드시 선언과 동시에 초기화를 해야 하며, 그렇지 않으면 컴파일 오류가 발생한다.
()
안에 다수의 변수를 넣어서 한 번에 선언 및 초기화가 가능하다.
const
도 가능하지만 당연히 초기화가 수반되어야 할 것이다.
package main
import (
"fmt"
)
var (
a int // 1개의 var + ()로 여러 변수 선언 가능
b int
c int
)
const (
d int = 1
e int = 2
f int = 3
)
func main() {
fmt.Println(a, b, c)
}
한 줄에 여러 개의 변수를 초기화 할 수 있다.
package main
import (
"fmt"
)
func main() {
a, b := 1, 2
fmt.Println(a, b) // 1 2
}
C++, Rust는 swap()
함수를 사용하고, Java는 tmp
변수를 만들어 두 변수의 값을 교환한다.
Go는 Python처럼 한 줄로 가능하다.
package main
import (
"fmt"
)
func main() {
a, b := 1, 2
b, a = a, b
fmt.Println(a, b) // 2 1
}