【问题标题】:Having trouble understanding interface/struct relationship难以理解接口/结构关系
【发布时间】:2018-10-31 23:45:47
【问题描述】:

我很难理解 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 的函数返回此类型时,我收到以下错误消息:

不能在返回参数中使用结果(用户类型)作为类型 []Datatype

我确定我遗漏了一些明显的东西,但在我看来这应该可行。

问题:

有人可以解释为什么它不起作用吗?是否有更好(更惯用)的方法来实现这一最终结果?

【问题讨论】:

  • String() 的实现在哪里?请注意,每次为类型设置别名时,它都是一个全新的类型。此外,您的结构和接口在这里没有关系 - 看来您正试图让您的 Users 类型实现接口。
  • 感谢您更新问题 - 您的 String() 方法应用于结构 User ...而不是类型 Users(复数..)。
  • 我用我的格式修复了一些错误并添加了一个关键的附加方法。那么在用户上实现我的界面是否有意义?我想我可以看到,但我仍然没有理解为什么它不能在用户上实现它并拥有它们的集合。
  • See the FAQ "我可以将 []T 转换为 []interface{} 吗?" (只需将您的[]users[]Datatype 替换为常见问题解答中的[]T[]interface{},这是同一个问题。)

标签: struct go


【解决方案1】:

切片不是协变的;尽管User 实现了Datatype[]User 没有实现[]Datatype(因为nothing 实现了[]Datatype:它本身不是接口类型,它只是一个切片类型,其元素类型是一个接口类型)。


编辑添加:正如Dave C 在上面的评论中指出的那样,Go FAQ 中出现了一个密切相关的问题。 [link] Go 常见问题解答以与 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
}

【讨论】:

  • 啊,好的。所以现在这有点道理。那么,您将如何创建一个可以返回不同但相关类型的集合的函数呢?这是一个相当常见的场景(基本多态性),我在 go 中遇到了麻烦。
  • 让你的函数返回一个[]Datatype
  • @RockyMountainHigh:我不知道协方差是否属于“基本多态性”;有许多语言对协方差有限制。不管 。 . .我建议在所有情况下都简单地返回[]Datatype。 ([]Datatypeelements 当然可以是Users。)此外,您还可以有一个 separate,类型特定的方法,即返回较窄类型的切片。
猜你喜欢
  • 2021-10-10
  • 1970-01-01
  • 2014-08-21
  • 2017-10-24
  • 2017-10-14
  • 1970-01-01
  • 2018-09-16
  • 2022-01-15
  • 1970-01-01
相关资源
最近更新 更多