我理解你的问题是关于接口和结构体之间关系的困惑。

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

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 (
&quot;encoding/csv&quot;
&quot;fmt&quot;
&quot;gopkg.in/validator.v2&quot;
&quot;reflect&quot;
&quot;strconv&quot;
&quot;time&quot;
)
type User struct {
Username      string `validate:&quot;nonzero&quot;`
UserId        string `validate:&quot;nonzero&quot;`
GivenName     string `validate:&quot;nonzero&quot;`
FamilyName    string `validate:&quot;nonzero&quot;`
Email         string `validate:&quot;regexp=^[0-9a-zA-Z]+@[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$&quot;`
SMS           string `validate:&quot;nonzero&quot;`
Phone         string `validate:&quot;min=10&quot;`
DateOfBirth   time.Time
}
type Users []User
func (u *User) Unmarshal(record []string) error {
s := reflect.ValueOf(u).Elem()
if s.NumField() != len(record) {
return &amp;FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i &amp;gt; s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case &quot;string&quot;:
f.SetString(record[i])
case &quot;int&quot;, &quot;int64&quot;:
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &amp;UnsupportedType{f.Type().String()}
}
}
return nil
}
func (u *User) String() string {
return fmt.Sprintf(&quot;%#v&quot;, 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(&quot;Validation error?: &quot;, 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
> }

huangapple
  • 本文由 发表于 2015年4月27日 11:32:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/29886723.html
匿名

发表评论

匿名网友

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

确定