본문 바로가기

Golang

Golang 1.22 Release 변경 내용 정리

해야지해야지 하다가 어느덧 시간이 훅 지나버린 1.22.0 Release 정리글 되시겠다. (Release note)


For loop

  • for loop내부의 변수는 한번 선언된 후 재사용 했었으나, 1.22에서는 매번 선언되도록 변경되었다.
  • 새로운 range를 제공한다.

이 개선들을 통해 아래의 두 로직은 같은 동작을 한다.

// go 1.21
func main() {
	var wg sync.WaitGroup
	for _, val := range []int{0, 1, 2, 3, 4} {
		wg.Add(1)
		go func(v int, pWG *sync.WaitGroup) {
			defer pWG.Done()
			fmt.Printf("%d ", v)
		}(val, &wg)
	}
	wg.Wait()
}

//////////////////////////////////////////////////////////////////////////////////////////////
// go 1.22
func main() {
	var wg sync.WaitGroup
	for val := range 5 {
		wg.Add(1)
		go func(pWG *sync.WaitGroup) {
			defer pWG.Done()
			fmt.Printf("%d ", val)
		}(&wg)
	}
	wg.Wait()
}

 


math/rand/v2 추가

기존 math/rand package의 Read(p []byte) (n int, err error)가 Deprecated되었다.

  • len(p)의 랜덤 byte를 할당하는 함수로 아래와 같은 기능을 제공했었다.
	randomBytes := make([]byte, 32)
	fmt.Println(randomBytes) // 모두 0
	rand.Read(randomBytes)
	fmt.Println(randomBytes) // 0~255사이의 랜덤값이 들어간다.

 

  • Deprecated된 이유를 따로 설명해주진 않았다. 하지만 해당 내용에서 언급한 The vast majority of calls to Read should use crypto/rand’s Read instead.란 말로 미루어 보아 rand.Read()를 사용하여 Key를 생성하는 경우를 막기 위함으로 생각된다.
  • rand.Read()는 time.Now().UnixNano()를 기반으로 하는 난수값을 생성한다. 즉, 예측 가능하다는 의미인데 이를 사용해서 key값을 생성해서 사용하면 보안에 문제가 될 수 있다.

Top-Level의 함수명이 변경되었다.

  • Intn, Int31, Int31n, Int63, Int63n → IntN, Int32, Int32N, Int64, Int64N.
    • 각각 31bit와 63bit integer를 return하지만, 정적 반환되는 type명은 int32와 int64었는데 통일되었다.

Unsigned 함수들이 추가되었다

  •  Uint32, Uint32N, Uint64, Uint64N, Uint, UintN.

Generic N함수가 추가되었다.

  • unsigned를 포함한 모든 integer type과, duration까지 지원한다.
 
{
    // random integer
    var max int32 = 100
    n := rand.N(max)
    fmt.Println("integer n =", n)
}

{
    // random unsigned integer
    var max uint64 = 100
    n := rand.N(max)
    fmt.Println("unsigned int n =", n)
}

{
    // random duration
    var max time.Duration = 100 * time.Millisecond
    n := rand.N(max)
    fmt.Println("duration n =", n)
}

 


go/version package 추가

Go version에 관련된 package가 추가되었다.

version의 확인, 비교, Validation check를 지원한다.

v := runtime.Version()
fmt.Println(version.IsValid(v))              //true
fmt.Println(version.Lang(v))                 // go1.22
fmt.Println(version.Compare(v, "go1.21rc2")) // 1
fmt.Println(version.Compare("go1.21rc2", v)) // -1

 


slices

Concat이 추가되었다.

s1 := []int{1, 2}
s2 := []int{3, 4}
s3 := []int{5, 6}
res := slices.Concat(s1, s2, s3)
fmt.Println(res) // {1, 2, 3, 4, 5, 6}

 

Delete, DeleteFunc, Compact, CompactFunc, Replace사용 시 src값의 길이가 변경되지 않고, 0이 할당되도록 변경되었다.

  • 이는 길이 변경 시 copy를 사용하여 element를 이동 시키는데 이 과정에서 잘려진 값들이 gc되지 못하여 Memory leak이 발생하고 있어 수정되었다. (#63393)
src := []int{11, 12, 13, 14}
// delete #1 and #2
mod := slices.Delete(src, 1, 3)
fmt.Println("src:", src)
// 1.21: 11, 12
// 여기서 2,3번 index의 값이 reference type인 경우 gc되지 않고 남는다.
// 1.22: 11, 14, 0, 0
fmt.Println("mod:", mod)
// 1.21: 11, 14
// 1.22: 11, 14
 

Routing pattern

GET /items/{id}

  • GET method만 한정하여 URL상에서 {id}와 같은 Segment가 매칭되어 Request value로 사용할 수 있다.
  • posts/{id}와 posts/latest와 같이 만약 두 API가 겹치는 경우는 most specific wins에 따라 매칭된다. posts/latest로 호출되면 경우는 후자가, posts/text가 호출 되면 전자가 호출 될 것이다.

/file/{path…}

  • 패턴의 마지막에 ...이 위치하는 경우 /file/*path와 같이 동작하게 된다.

/exact/match/{$}

  • 마지막이 /나 {$}로 끝나는 경우 모든 prefix를 가진 모든 url path와 매칭된다.
 

encoding/json

  • \u0008이 \b로, \u000c가 \f로 encoding된다.

     
func fixJSON(data []byte) []byte {
	data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1)
	data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1)
	data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)
	data = bytes.Replace(data, []byte("\\u0008"), []byte("\\b"), -1)
	data = bytes.Replace(data, []byte("\\u000c"), []byte("\\f"), -1)
	return data
}

참고