2015-04-27 3 views
2

У меня возникли трудности с пониманием взаимосвязи между интерфейсами и структурами в go. Я предрек интерфейс, который называется Тип данных:У меня возникли проблемы с пониманием взаимодействия interface/struct

 
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][email protected][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 
} 

Как вы можете видеть, у меня также есть тип пользователей, который просто [] Пользователь. Когда я пытаюсь вернуть этот тип из функции, которая имеет обратное tyoe из [] Datatype, я получаю следующее сообщение об ошибке:

cannot use results (type Users) as type []Datatype in return argument

Я уверен, что я пропускаю что-то очевидное, но, мне кажется, что это должно сработать. Может кто-нибудь объяснит мне, почему это не так? Есть ли лучший (более идиоматический путь) способ достичь этого конечного результата?

+1

Где ваша реализация 'String()'? Обратите внимание, что каждый раз, когда вы похожи на тип, это совершенно новый тип. Кроме того, ваша структура и интерфейс не имеют здесь отношения - похоже, вы пытаетесь реализовать свой «пользовательский» тип интерфейса. –

+1

Спасибо за обновление вопроса - ваш метод 'String()' применяется к struct 'User' ... не к типу' Users' (plural ..). –

+0

Я исправил некоторые goofs с моим форматированием и добавил дополнительный ключ. Имеет ли смысл реализовать мой интерфейс для пользователей? Наверное, я вижу это, но я все еще не понимаю, почему он не работает, реализуя это на пользователе и имея их коллекцию. – RockyMountainHigh

ответ

7

Ломтики не ковариантны; даже если User реализует Datatype, []User не реализует []Datatype (потому что ничего орудия []Datatype: она сама по себе не является типом интерфейса, это просто тип кусочек которого элемент тип представляет собой тип интерфейса).


Edited добавить: As Dave C пунктов в комментарии выше, тесно связанный с ним вопрос появляется в Go FAQ. [link] The Go Справка лицензирована таким образом, который совместит с содержимым стека обмена, так, вот вопрос в полном объеме:

Может ли я конвертировать [] T на [] интерфейс {}?

Непосредственно, потому что они не имеют одинакового представления в памяти. Необходимо скопировать элементы по отдельности в срез назначения. Этот пример преобразует кусочек int в ломтик interface{}:

t := []int{1, 2, 3, 4} 
s := make([]interface{}, len(t)) 
for i, v := range t { 
    s[i] = v 
} 
+0

А, ок. Итак, это имеет смысл. Итак, как бы вы хотели создать единую функцию, которая может возвращать коллекции разных, но связанных типов? Это довольно распространенный сценарий (базовый полиморфизм), что у меня возникает проблема «попасть» в ход. – RockyMountainHigh

+0

Просто возвращайте свои функции '[] Datatype'. – Volker

+0

@ RockyMountainHigh: Я не знаю, попадает ли ковариация под «базовый полиморфизм»; существует множество языков, которые имеют ограничения на ковариацию. Несмотря на . , , Я бы предложил просто вернуть '[] Datatype' во всех случаях.(Элементы * типа '[] Datatype', конечно же, могут быть' User's.) Кроме того, вы также можете использовать * отдельный * тип-тип, который возвращает кусок более узкого типа. – ruakh

Смежные вопросы