Функция, которая возвращает фрагмент пользовательского интерфейса - PullRequest
0 голосов
/ 30 апреля 2020

Сначала я хочу представить вас, потому что мне кажется, что мне не хватает какой-то базовой концепции Golang.

В моем приложении многие модели будут иметь метод с именем GetByUserId. Я создал интерфейс (UserCreatedEntity), который требует этот метод, поэтому я смогу создать фабрику методов контроллера GetUserRecords для каждого типа записей с помощью:

router.Handle("/ideas/mine",
        middlewares.AuthUser(controllers.GetMineFactory(&models.Idea{}))).Methods("POST")

router.Handle("/votes/mine",
        middlewares.AuthUser(controllers.GetMineFactory(&models.Vote{}))).Methods("POST")

router.Handle("/someNewType/mine",
        middlewares.AuthUser(controllers.GetMineFactory(&models.SomeNewType{}))).Methods("POST")

Вот так выглядит мой интерфейс:

type UserCreatedEntity interface {
    GetByUserId(userId uint) []UserCreatedEntity
}

И реализация:

func (idea *Idea) GetByUserId(userId uint) []UserCreatedEntity {
    ideas := []Idea{}
    GetDB().
        Table("ideas").
        /** Query removed to make code less confusing **/
        Scan(ideas)

    return ideas
}

Очевидно, что это не работает (версия со срезом указателей тоже не работает). Дело в том, что этот код будет работать, если я верну только одну запись - вот так (очевидно, с изменением подписи в интерфейсе):

func (idea *Idea) GetByUserId(userId uint) UserCreatedEntity {
    idea := &Idea{}
    GetDB().
        Table("ideas").
        /** Query removed to make code less confusing **/
        First(idea)

    return idea
}

Как сделать так, чтобы он работал как срез? Как я уже сказал, я подозреваю, что мне не хватает некоторых важных знаний. Такое глубокое объяснение было бы потрясающим.

Решение:

func (idea *Idea) GetByUserId(userId uint) []UserCreatedEntity {
    ideas := []*Idea{}
    GetDB().
        Table("ideas").
        Select("problems.name AS problem_name, ideas.id, ideas.problem_id, ideas.action_description, ideas.results_description, ideas.money_price, ideas.time_price, ideas.is_published").
        Joins("INNER JOIN problems ON ideas.problem_id = problems.id").
        Where("ideas.user_id = ?", userId).
        Scan(&ideas)

    uces := make([]UserCreatedEntity, len(ideas))
    for i, idea := range ideas {
        uces[i] = idea
    }
    return uces
}

Ответы [ 2 ]

3 голосов
/ 30 апреля 2020

В теории языков программирования это называется дисперсия , а не поддерживается в Go. Для более подробной информации см. это предложение .

В частности, типы возвращаемых данных не являются ковариантными. Срез T не реализует срез I, даже если T реализует I.

В приведенной выше записи часто задаваемых вопросов предлагается этот обходной путь:

Это необходимо скопировать элементы индивидуально в целевой слайс. В этом примере преобразуется фрагмент int в фрагмент интерфейса {}:

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

Хотя в вашем случае правильное решение может быть другим.

1 голос
/ 30 апреля 2020

Интерфейсы динамические c. Составные типы, которые включают интерфейсы, не являются.

UserCreatedEntity является интерфейсом, а Idea удовлетворяет интерфейсу, поэтому вы можете вернуть Idea из функции, сигнатура которой имеет тип возврата UserCreatedEntity .

[]UserCreatedEntity - это фрагмент UserCreatedEntity, а не интерфейс. Единственный тип, который может быть возвращен - []UserCreatedEntity. []Idea - это другой тип (фрагмент Idea). Вы можете заполнить []UserCreatedEntity Idea элементами , потому что каждый элемент имеет тип UserCreatedEntity, который снова является интерфейсом, и Idea разрешен там.

Аналогично, func() UserCreatedEntity - это тип "функция, которая возвращает UserCreatedEntity". Вы не можете заменить func() Idea, потому что это другой тип. Но вы можете вернуть Idea из func() UserCreatedEntity, потому что Idea - это UserCreatedEntity.

Если вы не использовали здесь Scan, который предположительно использует отражение, исправление будет объявить ваш локальный фрагмент как []UserCreatedEntity вместо []Idea. Поскольку вы используете Scan, вместо этого вы должны выполнить сканирование в []Idea, а затем выполнить итерацию по нему, чтобы скопировать все элементы в []UserCreatedEntity и вернуть его.

...