为什么我可以键入别名函数并使用它们而不进行转换?

Why can I type alias functions and use them without casting?

在Go中,如果定义了新类型,例如:

1
type MyInt int

然后不能将MyInt传递给期望int的函数,反之亦然:

1
2
3
4
5
6
7
8
func test(i MyInt) {
    //do something with i
}

func main() {
    anInt := 0
    test(anInt) //doesn't work, int is not of type MyInt
}

好的。但为什么同样的情况不适用于函数呢?例如。:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type MyFunc func(i int)
func (m MyFunc) Run(i int) {
    m(i)
}

func run(f MyFunc, i int) {
    f.Run(i)
}

func main() {
    var newfunc func(int) //explicit declaration
    newfunc = func(i int) {
        fmt.Println(i)
    }
    run(newfunc, 10) //works just fine, even though types seem to differ
}

现在,我没有抱怨,因为这样我就不用像在第一个例子中那样显式地将newfunc强制转换为MyFunc类型了;这似乎不一致。我相信这是有充分理由的,有人能启发我吗?

我之所以问这个问题,主要是因为我想用这种方式缩短一些相当长的函数类型,但我想确保这样做是可以预期的和可以接受的:)


事实证明,这是我对Go如何处理类型的误解,可以通过阅读规范的相关部分来解决:

http://golang.org/ref/spec_type_标识

我不知道的相关区别是命名和未命名类型的区别。

命名类型是具有名称的类型,例如int、int64、float、string、bool。此外,使用"type"创建的任何类型都是命名类型。

未命名的类型是那些类型,如[]string、map[string]string、[4]int。它们没有名称,只是与它们的结构相对应的描述。

如果比较两个命名类型,则名称必须匹配才能互换。如果比较一个命名类型和一个未命名类型,那么只要底层表示匹配,就可以了!

例如,给定以下类型:

1
2
3
4
type MyInt int
type MyMap map[int]int
type MySlice []int
type MyFunc func(int)

以下内容无效:

1
2
3
var i int = 2
var i2 MyInt = 4
i = i2 //both named (int and MyInt) and names don't match, so invalid

以下是好的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
is := make([]int)
m := make(map[int]int)
f := func(i int){}

//OK: comparing named and unnamed type, and underlying representation
//is the same:
func doSlice(input MySlice){...}
doSlice(is)

func doMap(input MyMap){...}
doMap(m)

func doFunc(input MyFunc){...}
doFunc(f)

我有点失望,我不知道这么快,所以我希望这澄清了类型百灵鸟有点为别人!这意味着比我一开始想的要少得多。


这个问题和答案都很有启发性。然而,我想提出一个在莱特努斯的回答中不清楚的区别。

  • 命名类型与未命名类型不同。

  • 命名类型的变量可以分配给未命名类型的变量,反之亦然。

  • 不同命名类型的变量不能相互赋值。

http://play.golang.org/p/uayhenoft9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import (
   "fmt"
   "reflect"
)

type T1 []string
type T2 []string

func main() {
    foo0 := []string{}
    foo1 := T1{}
    foo2 := T2{}
    fmt.Println(reflect.TypeOf(foo0))
    fmt.Println(reflect.TypeOf(foo1))
    fmt.Println(reflect.TypeOf(foo2))

    // Output:
    // []string
    // main.T1
    // main.T2

    // foo0 can be assigned to foo1, vice versa
    foo1 = foo0
    foo0 = foo1

    // foo2 cannot be assigned to foo1
    // prog.go:28: cannot use foo2 (type T2) as type T1 in assignment
    // foo1 = foo2
}