Why do we need generics in Go?







This article is about what it means to add generics to Go, and why I think we should do this. I will also touch on a possible change in the architecture of the language in order to add generics.



Go was released on November 10, 2009. Less than a day later, the first comment about generics appeared . It also mentions exceptions that we added to the language in the form of panic and recover at the beginning of 2010.



For three years of observation, the absence of generics has always been a list of three main problems that need to be fixed in the language.



Why are generics needed?



What does adding generics mean and why do we want it?



To paraphrase Jazayeri and others : programming with generics allows you to represent functions and data structures as generics, excluding types.



What does this mean?



Suppose we need to represent the elements of the slice in the reverse order. This is not a very common task, but also not so rare.



Suppose we have a slice of integers.



func ReverseInts(s []int) {
    first := 0
    last := len(s)
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
}
      
      





. . , . , .



func ReverseInts(s []int) {
    first := 0
    last := len(s) - 1
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
}
      
      





1, last



. .



func ReverseStrings(s []string) {
    first := 0
    last := len(s) - 1
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
}
      
      





ReverseInts



ReverseStrings



, , , . .



Go, Reverse



, .



.



Python JavaScript . Go , , .



, ++, Java, Rust Swift, .



Go



Go?



, (interface type) , . sort.Sort



.



, Go — . . , , , .



. . . , , , , , - , . — , , .



, , , - . Go , , , , Index



, . , , . -, , map' . Go , . .



- Reverse



, reflect, . , .



, Reverse



. . , Reverse



, - , , .



, , , Go, . , , , , . .



, , , , . , . , Go . .



Go . , . .



Go



, Go, Reverse



. . , Go .



open source, , - Reverse



, .



, «» . «» , . , , ++, , .

Reverse



, , , :





. , , ++.



, Go .





, . Go. , .



, . , . , , , .



Go - : map. , , . , . , []int



, , , .



map' — - , . :





-, , , , map: , , , .



, , - .



, Reverse



: - , , , . map, , , , .



Go . , .



, , .





Big Rock Candy Mountain, , . . , Go . , .



Go , , . , , , . .



, , .





. , .



-,

, -. , . , -, - , . -.





- , . , , , . , .





Go. C . .



Go

, Go . Go . . , , - .



Go. : , , Go .





, . , , Go.



Gophercon (Robert Griesemer) Go. , .



Reverse



.



func Reverse (type Element) (s []Element) {
    first := 0
    last := len(s) - 1
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
}
      
      





, .



. Element



(type parameter). , , , .



, , , .



func ReverseAndPrint(s []int) {
    Reverse(int)(s)
    fmt.Println(s)
}
      
      





(int)



, Reverse



.



, , , , .



- .



func ReverseAndPrint(s []int) {
    Reverse(s)
    fmt.Println(s)
}
      
      





- Reverse



ReverseInts



ReverseStrings



, , .





Go , . , -, - .



Reverse



. Element



, Go. -, , .



.



func IndexByte (type T Sequence) (s T, b byte) int {
    for i := 0; i < len(s); i++ {
        if s[i] == b {
            return i
        }
    }
    return -1
}
      
      





bytes strings IndexByte



. b



s



, s



[]byte



. - bytes strings. , .



, T



[]byte



. len



, byte.



T



. , , - , T



. Sequence



. .



Sequence



.



contract Sequence(T) {
    T string, []byte
}
      
      





, : T



[]byte



. , . .



, , Gophercon 2018, , . , , . , .



/ . .





, String



[]string



s



.



func ToStrings (type E Stringer) (s []E) []string {
    r := make([]string, len(s))
    for i, v := range s {
        r[i] = v.String()
    }
    return r
}
      
      





: , String



.



, String



. Stringer



.



contract Stringer(T) {
    T String() string
}
      
      





, T



String



.



, fmt.Stringer



, , ToStrings



fmt.Stringer



. - , fmt.Stringer



. fmt.Stringer



, Go . , fmt.Stringer



.





.



type Graph (type Node, Edge G) struct { ... }

contract G(Node, Edge) {
    Node Edges() []Edge
    Edge Nodes() (from Node, to Node)
}

func New (type Node, Edge G) (nodes []Node) *Graph(Node, Edge) {
    ...
}

func (g *Graph(Node, Edge)) ShortestPath(from, to Node) []Edge {
    ...
}
      
      





, . . , Node



Edges



, , Node



. Edge



Nodes



, Nodes



, Edge



.



, New



, Graph



, ShortestPath



Graph



.



, - . .



(Ordered types)



Go Min



. , Max



. , Min



, .



Min



, . :



func Min (type T Ordered) (a, b T) T {
    if a < b {
        return a
    }
    return b
}
      
      





Ordered



, T



, « », « », .



contract Ordered(T) {
    T int, int8, int16, int32, int64,
        uint, uint8, uint16, uint32, uint64, uintptr,
        float32, float64,
        string
}
      
      





Ordered



— , . , , - . , « ».



, « », , . , Go .



, , , -, . , -, , . - , , .



, , . Min



(, , ) . Ordered



, .



func Min (type T contracts.Ordered) (a, b T) T {
    if a < b {
        return a
    }
    return b
}
      
      





-



, - , . , .



type Tree (type E) struct {
    root    *node(E)
    compare func(E, E) int
}

type node (type E) struct {
    val         E
    left, right *node(E)
}
      
      





. New



.



func New (type E) (cmp func(E, E) int) *Tree(E) {
    return &Tree(E){compare: cmp}
}
      
      





, v



, , .



func (t *Tree(E)) find(v E) **node(E) {
    pn := &t.root
    for *pn != nil {
        switch cmp := t.compare(v, (*pn).val); {
        case cmp < 0:
            pn = &(*pn).left
        case cmp > 0:
            pn = &(*pn).right
        default:
            return pn
        }
    }
    return pn
}
      
      





, , . , - .



, .



func (t *Tree(E)) Contains(v E) bool {
    return *t.find(e) != nil
}
This is the code for inserting a new value.
func (t *Tree(E)) Insert(v E) bool {
    pn := t.find(v)
    if *pn != nil {
        return false
    }
    *pn = &node(E){val: v}
    return true
}
      
      





E



. - . , Go, , .



.



var intTree = tree.New(func(a, b int) int { return a - b })

func InsertAndCheck(v int) {
    intTree.Insert(v)
    if !intTree.Contains(v) {
        log.Fatalf("%d not found after insertion", v)
    }
}
      
      





. - , . -- .





, . , , , . , , .



CL, go/types. , , , . , , .



, -, , . , , . , , - . , .



, , , Go. . , .



— , -, , Go. , — , , , , . , - Go.



All Articles