2012년 1월 29일 일요일

Go 1 Preview

구글에서 Go 언어를 공식적으로 발표한지 2년이 조금 넘었다. 실제로 내부적으로 아이디어를 내고 프로토타입을 만들며 개발을 진행한 기간까지 합치면 4년이 조금 넘은 셈이다.

Go는 release 버전과 weekly 버전 두 가지로 나뉘어져 있는데, release 버전은 안정 버전에 해당하고 weekly 버전은 개발 버전에 해당한다고 볼 수 있다. 현재 release 버전은 r60.3이고 weekly 버전은 weekly.2012-01-20 인데, r60.3 버전의 Go 언어 공식 사이트는 잘 알다시피 http://golang.org 이지만, weekly 버전의 새로운 문서나 링크, 패키지들의 정보는 http://weekly.golang.org 에서 볼 수 있다.

2012년 상반기에 Go version 1 (줄여서 Go 1)이 나올 예정인데, 이 Go 1에 적용될 내용들이 weekly 버전에 지속적으로 반영이 되고 있다.
다른 프로그래밍 언어와 마찬가지로 Go 1도 버그 픽스등이 있으면 1.1, 1.2로 버전이 올라갈 수 있고, 혹은 디자인의 큰 변화에 따라 2가 될 수도 있을 것이다. Go 1에 대한 자세한 내용은 아래 두 링크를 통해 확인해보길 바라고, 이 포스트에서는 Go 1 Release Notes를 바탕으로 기존 r60.3 버전과의 차이점을 테스트해보고 정리한 내용을 공유해볼까 한다.

A Preview of Go version 1: http://blog.golang.org/2011/10/preview-of-go-version-1.html
Plan for Go 1: http://goo.gl/HzKZs

여기에 나오는 예제 소스는  https://github.com/golanger/go1_preview 에서 받을 수 있다.


언어의 변경

Append (append.go)
func append(slice []Type, elems ...Type) []Type
append는 위 시그너처와 같이 특정 타입 슬라이스에 같은 타입의 원소를 가변인자로 주어 그 슬라이스에 원소를 계속 추가할 수 있는 내장 함수다. 그런데 Go 1부터는 []byte에 string 타입을 append 하기 위해 그 string 타입을 []byte 타입으로 형변환을 하지 않아도 된다.


Close (close.go)

내장 함수 close는 channel에서 더 보낼 것이 없다는 것을 sender가 receiver에게 알리는 역할을 하는데, Go 1부터는 receive-only channel에서 close를 할 수 없게 된다(컴파일 에러). Go 1 이전에는 런타임 시에만 에러가 났다.
var c chan int
var csend chan<- int = c    // send only
var crecv <-chan int = c    // receive only
close(c)       // legal
close(csend)   // legal
close(crecv)   // illegal

Composite literals (composite_literals.go)

array, slice, map 타입의 composite literal은 그것이 포인터 타입이더라도 초기값을 위한 타입 지정을 생략할 수 있다.


Goroutines during init (gorouotines_during_init.go)

Go 1 이전의 패키지 초기화(init() 함수)에서 새로 만든 goroutine은 생성만 되고 실행은 모든 init()이 끝난 뒤에 되었다. Go 1에서는 init() 함수에서 초기화가 일어나는 도중에 goroutine들이 생성되고 실행도 될 수 있다.
var PackageGlobal int

func init() {
        c := make(chan int)
        go initializationFunction(c)
        PackageGlobal = <-c
}
이렇게 Go 1에서 goroutine들은 init 내에서 실행될 수 있기 때문에, deadlock 되지 않고 전역 변수에 초기화하는 표현도 가능하긴 하다. 하지만 main 전에 아직 실행중이지 않을 수도 있는 goroutine들에 의존하는 코드를 init 안에 넣는 것은 좋은 방법은 아니다.


The rune type (the_rune_type.go)

Unicode code point를 나타내는데 사용되는 새로운 rune이라는 타입이 생겼다. byte가 uint8의 alias 이듯, rune은 int32의 alias이다.
‘a’, ‘뷁’, ‘\uAC00’ 은 기본 타입으로 rune이 된다. (1.0이 기본 타입으로 float64가 되는 것처럼)


The error type (error.go)
빌트인 인터페이스 타입으로 error 타입이 추가되었고, 기존 os.Error는 제거되었다.
// Before Go 1 (os.Error)
type Error interface {
        String() string
}

// After Go 1 (built-in type)
type error interface {
        Error() string
}
기존에는 os 패키지의 os.Error 인터페이스를 사용했었는데, String() 메서드를 구현하는 대신 Error() 메서드를 구현해서 fmt 패키지에서 자동으로 Error()를 호출하는 것으로 바뀌었다.

기존의 Error 인터페이스를 리턴하는 os.NewError()가 새로운 errors 라는 패키지의 error 타입을 리턴하는 New() 함수로 대체된다.

func New(text string) error  // errors 패키지
// Before Go 1
os.NewError(“error message”)  // os.Error 리턴

// After Go 1
var ErrSyntax = errors.New("syntax error")  // error 리턴

에러 비교하기
Before Go 1:  os.NewError("foo") == os.NewError("foo")
After Go 1:  errors.New("foo") != errors.New("Foo")

두 개의 error string을 비교하려면 아래처럼 해야 한다.

err1.Error() == err2.Error()


Deleting from maps (deleting_from_maps.go)
map 안의 원소 하나를 삭제하기 위해, delete 내장 함수가 추가되었다. 기존 방식은 컴파일 되지 않음
// Before Go 1
m[“Jan”] = -1, false

// After Go 1
delete(m, “Jan”)

Iterating in maps

for range 문장을 사용해서 map 안의 원소들을 반복해서 접근할 때 반드시 그 순서가 동일한 순서로 map을 iteration 한다고 가정할 수 없다.
m := map[string]int{"Sunday": 0, "Monday": 1}
for name, value := range m {
        // This loop should not assume Sunday will be visited first.
        fmt.Println(name, value)
}

Multiple assignment (multiple_assignment.go)
sa := []int{1, 2, 3}
i := 0
i, sa[i] = 1, 2   // sets i = 1, sa[0] = 2

// Go 1 이전에는 왼쪽 i가 먼저 1이 되고 sa[i] 즉, sa[1]이 2가 할당되어 {1, 2, 3} 이 된다.
// Go 1에서는 {2, 2, 3}

Returns and shadowed variables (returns_and_shadowed_variables.go)

shadowed variable이라는 건 내부 범위(inner scope)내에서 또 다른 변수로 같은 변수 이름을 가지는 것. 하지만, Go 1에서는 named return parameter와 같은 이름을 인자없이 return 되는 시점에 그 named return parameter와 같은 이름의 변수가 재선언 되어있다면 (shadowed 되었다면) 컴파일러 에러가 난다.

Copying structs with unexported fields
(my_new_struct_pkg.go,  copying_structs_with_unexported_fields.go)

Go 1은 struct 내 필드가 소문자로 시작하는 unexported 필드들에 대해서도 할당과 복사가 가능하도록 규칙을 완화했다. 하지만, 복사되었다고 해서 unexported 필드에 직접 접근할 수 있지는 않다.


Equality of structs and arrays (equality_of_structs_and_arrays.go)

Go 1에서는 struct와 array의 값들에 대해 equality (==), inequality (!=) 를 사용할 수 있다.
그래서, map의 key로 struct나 array를 써서 같은 키값의 map value를 가져올 수 있다.



Function and map equality (function_and_map_equality.go)

Go 1에서는 function과 map에서 서로 같은지 비교는 컴파일 에러가 난다. 대신 nil 과의 비교는 가능하다. Go 1 이전에는 가능했던 부분이다.



라이브러리의 변경

The package hierarchy

패키지 구조가 일부 변경되었다. (http가 net/http 밑으로 등.. 이런 변경 부분은 gofix로 모두 수정이 가능하다)
http://weekly.golang.org/doc/go1.html#hierarchy

System call errors
syscall 패키지는 시스템 콜 에러 시, integer errno 값 대신에 syscall.Errno (error 인터페이스를 구현한)를 리턴한다.
기존 os.Errno (int64) 을 리턴하는 대신에 syscall.Errno (uintptr)를 리턴하는데, 이 타입이 새로운 error 타입의 Error()를 구현하고 있다.

Time (time.go)
Go 1 에서 완전히 새롭게 디자인되었다.
int64의 나노초 정수와 년도 시간 등을 다루는 *time.Time 대신에 time.Time과 time.Duration 타입이 생겼다.
Time은 고대 과거부터 먼 미래까지 표현할 수 있고, Duration은 약 290년 정도의 기간을 가진다.

The html package
HTML5에 대한 full parser 지원

The http package
리팩토링 되었고, 몇 가지 유틸리티들은 httputil 서브디렉토리로 옮겨졌다.

The strconv package
기본 타입의 string 표현으로 변환 혹은 string에서 기본 타입 표현으로 변환 해주는 strconv 패키지가 C 스타일에서 Go 스타일로 작성되었다. http://weekly.golang.org/doc/go1.html#strconv


이 외에도 Makefile 작성 없이도 좀 더 편리하게 Go 소스 파일들을 빌드할 수 있는 go 라는 명령어가 추가되었고, 수많았던 HelpWanted 항목들이 속속 구현, 해결되고 있다.

국내에서도 Go 언어에 대해 좀 더 같이 이야기해볼 것들이 많아졌으면 좋겠고, 실질적인 프로젝트 구현 사례도 많이 나오길 기대해본다.

작성: Go 언어 한국 커뮤니티 (http://golang.kr) 김종민
(이 글은 Golang Korea Team Blog에도 같이 게시 되었습니다.)

댓글 2개:

  1. 간단한 정리.....

    아아 한글이라 너무 좋다..ㅋㅋㅋ

    답글삭제
  2. Oh my goodness! Іmprеsѕivе article ԁudе!

    Μаny thаnkѕ, Hoωеνеr I am going thгоugh problems with уour RSS.
    I don't understand the reason why I cannot join it. Is there anyone else having similar RSS problems? Anyone that knows the answer will you kindly respond? Thanx!!

    Feel free to surf to my web page :: payday loans

    답글삭제