[]fs.FileInfo 无法作为函数参数传递,该函数接受自定义接口。

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

[]fs.FileInfo is not able to be passed in as function parameter which accepts custom interface

问题

在下面的代码中,第一个printAll出现了编译错误./main.go:10:7: cannot use info (type []fs.FileInfo) as type fileInfoList in argument to print。为什么会出现这种情况?难道不应该满足fileInfo接口,因为每个fs.FileInfo类型都有一个Name方法?

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	info, _ := ioutil.ReadDir("./")

	// info的类型是[]fs.FileInfo

	/*
		type FileInfo interface {
			Name() string
			// ...
		}
	*/

	printAll(info) // 无法工作

	var list []fileInfo
	for _, f := range info {
		list = append(list, f)
	}
	printAll(list) // 可以工作

	d1 := defaultFileInfo{}
	d2 := defaultFileInfo{}
	dList := []fileInfo{&d1, &d2}
	printAll(dList) // 可以工作
}

type fileInfo interface {
	Name() string
}

func printAll(fileInfoList []fileInfo) {
	for _, f := range fileInfoList {
		fmt.Println(f.Name())
	}
}

type defaultFileInfo struct{}

func (d *defaultFileInfo) Name() string {
	return "..."
}

在这段代码中,printAll(info)无法工作的原因是,info的类型是[]fs.FileInfo,而不是[]fileInfo。尽管fs.FileInfo类型实现了Name方法,但它并不是fileInfo接口的实现。因此,无法将info直接传递给printAll函数。

为了解决这个问题,我们需要创建一个新的切片list,将info中的元素转换为fileInfo类型,并将其传递给printAll函数。这样,printAll(list)就可以正常工作了。

另外,代码中还定义了一个defaultFileInfo结构体,并为其实现了Name方法。通过创建dList切片并将defaultFileInfo类型的实例添加到其中,我们可以将dList传递给printAll函数,并且它也可以正常工作。

英文:

In the below code the first printAll has a compile error ./main.go:10:7: cannot use info (type []fs.FileInfo) as type fileInfoList in argument to print. How come this is the case? Shouldn't the fileInfo interface be met since each fs.FileInfo type has a Name method?

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	info, _ := ioutil.ReadDir("./")

	// info is of type []fs.FileInfo

	/*
		type FileInfo interface {
			Name() string
			// ...
		}
	*/

	printAll(info) // DOESN'T WORK

	var list []fileInfo
	for _, f := range info {
		list = append(list, f)
	}
	printAll(list) // works

	d1 := defaultFileInfo{}
	d2 := defaultFileInfo{}
	dList := []fileInfo{&d1, &d2}
	printAll(dList) // works
}

type fileInfo interface {
	Name() string
}

func printAll(fileInfoList []fileInfo) {
	for _, f := range fileInfoList {
		fmt.Println(f.Name())
	}
}

type defaultFileInfo struct{}

func (d *defaultFileInfo) Name() string {
	return "..."
}

答案1

得分: 2

你是正确的,fs.FileInfo 实现了接口 fileInfo

然而,这并不意味着你可以将类型为 []fs.FileInfo 的值赋给类型为 []fileInfo 的变量 - 这些是完全不同的类型。

事实上 - 这些类型甚至不能相互转换,它们在内存中的布局完全不同:接口值是一对 {具体类型,数据结构指针},而结构体值就是你在结构体中看到的内容!

所以,简短的答案是你必须像你的循环一样为每个结构体切片元素分配值并将其附加到接口值切片中... 在幕后,Go 会自动为你为每个结构体切片元素创建一个接口值。

简洁地说,"Go 接口类型与实现它们的结构体类型是协变的,但接口值的切片与实现这些值的结构体切片不是类型协变的。"

有关结构体切片与接口类型的更多信息,请参阅https://www.timr.co/go-interfaces-the-tricky-parts/

英文:

You're correct, fs.FileInfo implement the interface fileInfo!

However, that does not mean that you can assign a value of type []fs.FileInfo to a variable typed []fileInfo - these are completely different types.

In fact - the types cannot even be converted to each other, they are laid out completely differently in memory: interface values are a pair of {concrete type,data struct pointer}, and struct values are just what you see in the struct!

So, the short answer is that you have to do something like your loop which assigns values and appends them to the slice of interface values... behind the scenes what is happening is Go is creating an interface value for each of the struct slice elements for you, automatically.

A succinct way to say this all is: "Go interfaces types are covariant with the struct types that implement them, but slices of interface values are not type-covariant with slices of structs that implement those values."

For more info on slices of structs vs. interface types, see https://www.timr.co/go-interfaces-the-tricky-parts/

huangapple
  • 本文由 发表于 2021年6月12日 13:42:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/67945979.html
匿名

发表评论

匿名网友

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

确定