为什么我可以定义别名函数并在使用时不需要进行类型转换?

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

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

问题

在Go语言中,如果你定义了一个新的类型,比如:

type MyInt int

那么你不能将MyInt类型的值传递给期望int类型的函数,反之亦然:

func test(i MyInt) {
    // 使用i做一些操作
}

func main() {
    anInt := 0
    test(anInt) // 无法通过编译,int类型不是MyInt类型
}

好的。但是为什么对函数却不适用呢?比如:

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) // 显式声明
    newfunc = func(i int) {
        fmt.Println(i)
    }
    run(newfunc, 10) // 完全正常工作,尽管类型似乎不同
}

现在,我并不是在抱怨,因为这样做可以避免我在第一个例子中需要显式将newfunc转换为MyFunc类型,但这似乎是不一致的。我相信这样做有一个很好的理由,有人能给我解释一下吗?

我之所以问这个问题,主要是因为我想缩短一些相当长的函数类型,但我想确保这样做是符合预期且可接受的 为什么我可以定义别名函数并在使用时不需要进行类型转换?

英文:

In Go, if you define a new type e.g.:

type MyInt int

You can't then pass a MyInt to a function expecting an int, or vice versa:

func test(i MyInt) {
    //do something with i
}

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

Fine. But why is it then that the same does not apply to functions? e.g.:

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
}

Now, I'm not complaining because it saves me having to explicitly cast newfunc to type MyFunc, as I would have to do in the first example; it just seems inconsistent. I'm sure there is a good reason for it; can anyone enlighten me?

The reason I ask is mainly because I would like to shorten some of my rather long function types in this way, but I want to make sure it's expected and acceptable to do this 为什么我可以定义别名函数并在使用时不需要进行类型转换?

答案1

得分: 177

原来,这是我对Go语言处理类型的一种误解,可以通过阅读规范中相关部分来解决:

http://golang.org/ref/spec#Type_identity

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

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

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

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

例如,给定以下类型:

type MyInt int
type MyMap map[int]int
type MySlice []int
type MyFunc func(int)

以下是无效的:

var i int = 2
var i2 MyInt = 4
i = i2 //两个命名类型(int和MyInt)的名称不匹配,因此无效

以下是有效的:

is := make([]int)
m := make(map[int]int)
f := func(i int){}

//OK: 比较命名和未命名类型,并且底层表示相同:
func doSlice(input MySlice){...}
doSlice(is)

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

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

我有点遗憾我之前不知道这一点,所以我希望这能为其他人澄清类型的问题!这意味着比我最初想的要少得多的类型转换 为什么我可以定义别名函数并在使用时不需要进行类型转换?

英文:

Turns out, this is a misunderstanding that I had about how Go dealt with types, which can be resolved by reading the relevant part of the spec:

http://golang.org/ref/spec#Type_identity

The relevant distinction that I was unaware of was that of named and unnamed types.

Named types are types with a name, such as int, int64, float, string, bool. In addition, any type you create using 'type' is a named type.

Unnamed types are those such as []string, map[string]string, [4]int. They have no name, simply a description corresponding to how they are to be structured.

If you compare two named types, the names must match in order for them to be interchangeable. If you compare a named and an unnamed type, then as long as the underlying representation matches, you're good to go!

e.g. given the following types:

type MyInt int
type MyMap map[int]int
type MySlice []int
type MyFunc func(int)

the following is invalid:

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

the following is fine:

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)

I'm a bit gutted I didn't know that sooner, so I hope that clarifies the type lark a little for someone else! And means much less casting than I at first thought 为什么我可以定义别名函数并在使用时不需要进行类型转换?

答案2

得分: 20

问题和答案都非常有启发性。然而,我想提出一个在lytnus的回答中不太清楚的区别。

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

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

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

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可以赋值给foo1,反之亦然
	foo1 = foo0
	foo0 = foo1
	
	// foo2不能赋值给foo1
	// prog.go:28: cannot use foo2 (type T2) as type T1 in assignment
	// foo1 = foo2
}
英文:

Both the question and answer are pretty enlightening. However, I'd like to bring up a distinction which is not clear in lytnus's answer.

  • Named Type is different from Unnamed Type.

  • Variable of Named Type is assignable to variable of Unnamed Type, vice versa.

  • Variable of different Named Type is not assignable to each other.

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

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
}

答案3

得分: 1

如果最终(作为一种类型的类型,可能是一个案例)的基础类型是原始类型,GO将不允许直接将一个类型的变量直接赋值给另一个类型的变量,反之亦然。然而,可以将值转换为目标类型并使用。

package main
import (
	"fmt"
)

type T int

type U T

type V U

type UU U

func main() {

	var s int 
    var t T   
    var u U
    var uu UU

	s = 9
	t = 10
	u = 11
	uu = 111

	fmt.Println(s)
	fmt.Println(t)
	fmt.Println(u)
	fmt.Println(uu)

	fmt.Println("========")

	//s = t
	//u = s
	//v = s

	u = (U)(uu)

	//fmt.Println(s)
	//fmt.Println(u)
	fmt.Println(u)

}
英文:

If the ultimate(as type of a type maybe a case)underlying type is a primitive type GO will not allow direct assignment of variable of one type to the another type directly and vice versa. However the value can be casted to the destination type and used.

package main
import (
	"fmt"
)

type T int

type U T

type V U

type UU U

func main() {

	var s int 
    var t T   
    var u U
    var uu UU

	s = 9
	t = 10
	u = 11
	uu = 111

	fmt.Println(s)
	fmt.Println(t)
	fmt.Println(u)
	fmt.Println(uu)

	fmt.Println("========")

	//s = t
	//u = s
	//v = s

	u = (U)(uu)

	//fmt.Println(s)
	//fmt.Println(u)
	fmt.Println(u)

}

答案4

得分: 0

像其他人说的那样,anInt := 0 选择了一个类型来表示 0,而不仅仅是一个数字。我所知道的唯一保持没有类型的方法是使用 const。如果你使用 const 而不是 :=,那么这个方法可以工作。

func main() {
    const anInt = 0
    test(anInt)
}
英文:

Like the other people said anInt := 0 chooses a type for 0 rather than just a number. The only way I know to keep it without a type is to use const. If you use const instead of := then this can work.

func main() {
    const anInt = 0
    test(anInt)
}

huangapple
  • 本文由 发表于 2013年10月12日 21:07:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/19334542.html
匿名

发表评论

匿名网友

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

确定