英文:
[]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/
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论