英文:
Having trouble understanding interface/struct relationship
问题
我在理解Go语言中接口和结构体之间的关系方面遇到了困难。我声明了一个名为Datatype
的接口,如下所示:
package main
type Datatype interface {
Unmarshal(record []string) error
String() string
}
我还创建了几个实现了这个接口的结构体。这里是一个简单的例子:
package main
import (
"encoding/csv"
"fmt"
"gopkg.in/validator.v2"
"reflect"
"strconv"
"time"
)
type User struct {
Username string `validate:"nonzero"`
UserId string `validate:"nonzero"`
GivenName string `validate:"nonzero"`
FamilyName string `validate:"nonzero"`
Email string `validate:"regexp=^[0-9a-zA-Z]+@[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$"`
SMS string `validate:"nonzero"`
Phone string `validate:"min=10"`
DateOfBirth time.Time
}
type Users []User
func (u *User) Unmarshal(record []string) error {
s := reflect.ValueOf(u).Elem()
if s.NumField() != len(record) {
return &FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case "string":
f.SetString(record[i])
case "int", "int64":
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &UnsupportedType{f.Type().String()}
}
}
return nil
}
func (u *User) String() string {
return fmt.Sprintf("%#v", u)
}
func (u *User) populateFrom(reader *csv.Reader) (Users, error) {
var users Users
for {
record, err := reader.Read()
check(err)
err = u.Unmarshal(record)
check(err)
valid := validator.Validate(u)
if valid == nil {
user := *u
users = append(users, user)
} else {
fmt.Println("Validation error?: ", valid)
}
}
return users, nil
}
问题:
正如你所看到的,我还有一个名为Users
的类型,它只是[]User
。当我尝试从一个返回类型为[]Datatype
的函数中返回这个类型时,我会得到以下错误信息:
cannot use results (type Users) as type []Datatype in return argument
我确定我漏掉了一些明显的东西,但在我看来,这应该是可以工作的。
问题:
有人能解释一下为什么它不起作用吗?有没有更好(更符合惯例)的方法来实现这个最终结果?
英文:
I am having difficulty understanding the relationship between interfaces and structs in go. I have declared an interface called Datatype
as follows:
package main
type Datatype interface {
Unmarshal(record []string) error
String() string
}
I have also created several structs that implement this interface. Here is one simple example:
package main
import (
"encoding/csv"
"fmt"
"gopkg.in/validator.v2"
"reflect"
"strconv"
"time"
)
type User struct {
Username string `validate:"nonzero"`
UserId string `validate:"nonzero"`
GivenName string `validate:"nonzero"`
FamilyName string `validate:"nonzero"`
Email string `validate:"regexp=^[0-9a-zA-Z]+@[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$"`
SMS string `validate:"nonzero"`
Phone string `validate:"min=10"`
DateOfBirth time.Time
}
type Users []User
func (u *User) Unmarshal(record []string) error {
s := reflect.ValueOf(u).Elem()
if s.NumField() != len(record) {
return &FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i &gt; s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case "string":
f.SetString(record[i])
case "int", "int64":
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &UnsupportedType{f.Type().String()}
}
}
return nil
}
func (u *User) String() string {
return fmt.Sprintf("%#v", u)
}
func (u *User) populateFrom(reader *csv.Reader) (Users, error) {
var users Users
for {
record, err := reader.Read()
check(err)
err = u.Unmarshal(record)
check(err)
valid := validator.Validate(u)
if valid == nil {
user := *u
users = append(users, user)
} else {
fmt.Println("Validation error?: ", valid)
}
}
return users, nil
}
Problem:
As you can see, I also have a type called Users
which is just []User
. When I try to return this type from a function that has a return type of []Datatype
, I get the following error message:
> cannot use results (type Users) as type []Datatype in return argument
I'm sure I'm missing something obvious but it seems to me that this should work.
Question:
Could someone please explain why it does not work? Is there a better (more idiomatic) way to achieve this end result?
答案1
得分: 8
切片不具备协变性;即使 User
实现了 Datatype
接口,[]User
并不实现 []Datatype
接口(因为没有任何东西实现了 []Datatype
接口:它本身不是一个接口类型,而只是一个元素类型为接口类型的切片类型)。
编辑补充: 正如 Dave C 在上面的评论中指出的,Go FAQ 中有一个相关的问题。[链接] Go FAQ 的许可方式与 Stack Exchange 内容兼容,所以这里是完整的问题:
> 我可以将 []T
转换为 []interface{}
吗?
>
> 直接转换是不可能的,因为它们在内存中的表示方式不同。需要逐个复制元素到目标切片。以下示例将一个 int
切片转换为一个 interface{}
切片:
>
> t := []int{1, 2, 3, 4}
> s := make([]interface{}, len(t))
> for i, v := range t {
> s[i] = v
> }
英文:
Slices are not covariant; even though User
implements Datatype
, []User
does not implement []Datatype
(because nothing implements []Datatype
: it itself is not an interface type, it's just a slice type whose element type is an interface type).
Edited to add: As Dave C points out in a comment above, a closely-related question appears in the Go FAQ. [link] The Go FAQ is licensed in a way that's compatible with Stack Exchange content, so, here's the question in its entirety:
> Can I convert a []T to an []interface{}?
>
> Not directly, because they do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int
to a slice of interface{}
:
>
> t := []int{1, 2, 3, 4}
> s := make([]interface{}, len(t))
> for i, v := range t {
> s[i] = v
> }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论