본문 바로가기

개발 노트/Go

[Golang] go 1.18 Released (generics)

반응형

드디어 Golang 에 generics (Templates for C++) 기능이 추가되었다.

golang 은 기본적으로 최소한의 keywords 로 complile 성능과 실행 성능을 높이기 위해 다른 언어에서 사용하는 다양한 개념들을 일부러 많이 포함시키지 않았다고 하는데, 이번 go 1.18 릴리즈에 template 기능이 추가되었다고 한다.

 

사용법을 익히기 위해, golang 에서 제공하는 generics tutorial 에 대해 정리해보려 한다.

 

기존 함수

golang 은 strictly typed language 이기 때문에 go 1.18 미만 generics 이 없던 버전에서는 동일한 기능의 함수더라도 입력 인자 인자와 함수 이름을 다르게 선언해 주어야 했다.

 

 1. main.go 파일 작성

package main

import "fmt"

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first":  34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first":  35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))
}

- SumInts 와 SumFloats 함수는 입력받은 map 의 value type 만 Int64 / Float64 만 다르고 동작은 동일하다

- 하지만 함수 입력 인자와 함수명을 다르게 선언해 주어야 한다.

2. 실행

$ go run main.go
Non-Generic Sums: 46 and 62.97

 

Generics

go 1.18 부터는 generics 기능이 추가되어 동일한 기능에 하나의 함수에도 여러 타입의 입력 인자를 받을 수 있다.

 

1. main.go 작성

package main

import "fmt"

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first":  34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first":  35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumIntsOrFloats[string, int64](ints),
        SumIntsOrFloats[string, float64](floats))
        
    fmt.Printf("Non-Generic Sums: %v and %v\n", 
        SumIntsOrFloats(ints), // can be ommitted type arguments
        SumIntsOrFloats(floats))
}

- SumIntsOrFloats 함수 이름과 입력 인자 사이에 '[', ']' 대괄호로 입력 받을 인자의 타입을 선언할 수 있다.

- 입력 인자의 타입을 선언할 때 '|' 기호로 입력 가능한 여러 타입을 나열하여 V type 으로 선언할 수 있다.

- V type 을 입력 인자와 출력 타입으로 선언하여 map 을 iterating 하면서 V 타입 인자의 합을 V 타입 반환 값으로 반환한다.

 

2. 실행

$ go run main.go
Non-Generic Sums: 46 and 62.97

type constraints

위 예시에서 여러 타입의 입력 인자로 선언한 V 를 Number 라는 type 의 묶음으로 선언할 수 있다.

 

1. type constraint 및 함수 선언

tpackage main

import "fmt"

type Number interface {
    int64 | float64
}

// SumNumbers sums the values of map m. It supports both integers
// and floats as map values.
func SumNumber[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first":  34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first":  35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumNumber(ints),
        SumNumber(floats))
}

2. main 함수 위 예시와 동일하게 작성하여 실행

$ go run main.go
Non-Generic Sums: 46 and 62.97

constraints package

constraints package 를 사용하면 사전 선언된 type 들을 사용하여 함수를 작성할 수 있다.

// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package constraints defines a set of useful constraints to be used
// with type parameters.
package constraints

// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
	Signed | Unsigned
}

// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
	~float32 | ~float64
}

// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
	~complex64 | ~complex128
}

// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
	Integer | Float | ~string
}

- ~ (물결표) 기호로 작성된 유형 근사(type approximation)를 사용하여 작성되어 있다.

- ~int와 같은 유형 근사는 int 자체뿐만 아니라 int에서 파생된 모든 유형과도 일치합니다.

- 위 package 를 import 해서 사용할 수 있다.

any type

any 를 사용하여 모든 타입에 상관없이 입력을 받도록 할 수 있다.

 

1. main.go 작성

package main

import "fmt"

func splitAnySlice[T any](s []T) ([]T, []T) {
    mid := len(s) / 2
    return s[:mid], s[mid:]
}

func main() {
    intSlice := []int{1, 2, 3, 4, 5}
    floatSlice := []float32{1.1, 2.2, 3.3, 4.4, 5.5}

    fmt.Println(splitAnySlice(intSlice))
    fmt.Println(splitAnySlice(floatSlice))

}

2. 실행

$ go run main.go
[1 2] [3 4 5]
[1.1 2.2] [3.3 4.4 5.5]

 

참고 링크

 

반응형

'개발 노트 > Go' 카테고리의 다른 글

[Golang] http.Client reuse connection  (0) 2020.10.05