Go中的通用函数

huangapple go评论85阅读模式
英文:

Generic Functions in Go

问题

我正在学习Go语言,文档和交互式课程中说,一个空的接口(empty interface)可以持有任何类型,因为它不需要额外实现的方法。

所以举个例子:

func describe(i interface{}) {
    fmt.Printf("Type: %T | Value: %v\n", i, i)
}

...会输出...

"Type: int | Value: 5" // 当 i := 5
"Type: string | Value: test" // 当 i := "test"
...等等

所以我的问题是,这是Go语言实现泛型函数的方式吗?还是还有其他更合适的方式?

英文:

I'm in the process of learning Go and the documentation and interactive lessons say that an empty interface can hold any type, as it requires no additionally implemented methods.

So for an example:

func describe(i interface{}) {
    fmt.Printf("Type: %T | Value: %v\n", i, i)
}

...would print out...

"Type: int | Value: 5" // for i := 5
"Type: string | Value: test" // for i := "test"
... etc

So I guess my question is if this is Go's way of implementing generic functions or if there is another, more suitable, way of doing them.

答案1

得分: 24

从Go 1.18开始,你可以按照下面的方式编写一个通用函数Print

package main

import (
	"fmt"
)

// T可以是任意类型
func Print[T any](s []T) {
	for _, v := range s {
		fmt.Print(v)
	}
}

func main() {
	// 传递字符串列表也可以工作
	Print([]string{"Hello, ", "world\n"})

	// 你也可以将整数列表传递给同一个函数
	Print([]int{1, 2})
}

输出结果:

Hello, world
12

更多信息请参考:1: https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md

英文:

As of Go 1.18 you can write a generic function Print as below:

package main

import (
	"fmt"
)

// T can be any type
func Print[T any](s []T) {
	for _, v := range s {
		fmt.Print(v)
	}
}

func main() {
	// Passing list of string works
	Print([]string{"Hello, ", "world\n"})

	// You can pass a list of int to the same function as well
	Print([]int{1, 2})
}

Output:

Hello, world
12

答案2

得分: 15

Go语言的范式通常是通过在非空接口中实现行为来避免这种情况。例如,假设你想要使用特定类型的格式打印某些内容:

func Print(i interface{}) {
    switch o := i.(type) {
        case int64:
            fmt.Printf("%5d\n", o)
        case float64:
            fmt.Printf("%7.3f\n", o)
        case string:
            fmt.Printf("%s\n", o)
        default: // 包括结构体等其他类型
            fmt.Printf("%+v\n", o)
    }
}

或者,你可以为那些知道如何将自己转换为字符串的对象定义一个接口(在基础库中作为fmt.Stringer存在),然后使用该接口:

type Stringer interface {
    String() string
}

func Print(o Stringer) {
    fmt.Println(o.String())
}

type Foo struct {
    a, b int
}

func (f Foo) String() string {
    // 让我们使用一个与%+v不同的自定义输出格式
    return fmt.Sprintf("%d(%d)", f.a, f.b) 
}

type Bar struct {
    t bool
}

func (b Bar) String() string {
    if b.t {
        return "TRUE! =D"
    }
    return "false =("
}

这样你就可以拥有类似泛型的功能,但仍保留类型安全性,并且行为本身由类型定义,而不是由你的通用函数定义。

Go鼓励你根据类型的行为来思考,它们能做什么,而不是它们包含了什么。

英文:

The Go paradigm is generally to avoid this by implementing the behavior in non-empty interfaces. For example, say you wanted to print something with type-specific formatting:

func Print(i interface{}) {
    switch o := i.(type) {
        case int64:
            fmt.Printf("%5d\n", o)
        case float64:
            fmt.Printf("%7.3f\n", o)
        case string:
            fmt.Printf("%s\n", o)
        default: // covers structs and such
            fmt.Printf("%+v\n", o)
    }
}

Alternatively, you could define an interface for things that know how to string themselves (this exists in the base library as an fmt.Stringer), and use that:

type Stringer interface {
    String() string
}

func Print(o Stringer) {
    fmt.Println(o.String())
}

type Foo struct {
    a, b int
}

func (f Foo) String() string {
    // Let's use a custom output format that differs from %+v
    return fmt.Sprintf("%d(%d)", f.a, f.b) 
}

type Bar struct {
    t bool
}

func (b Bar) String() string {
    if b.t {
        return "TRUE! =D"
    }
    return "false =("
}

https://play.golang.org/p/Ez6Hez6cAv

This lets you have a generic-like functionality, but still retain type safety and have the behavior itself defined by the types, rather than your generic function.

Go encourages you to think of types in this way, based on their behavior, what they can do rather than what they contain.

答案3

得分: 1

Golang没有泛型类型,所以解决这个问题的方法是传递interface类型,并在函数内部使用类型断言。

英文:

Golang doesn't have a generic type, so the way you get around this is passing interface type and using type switches within the function.

huangapple
  • 本文由 发表于 2017年3月11日 04:15:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/42726986.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定