Как заполнить фрагмент интерфейса, переданный как ссылка на функцию - PullRequest
0 голосов
/ 10 июня 2018

У меня есть небольшой пример, в котором я пытаюсь заполнить фрагмент [] Entry (где Entry является интерфейсом) внутри функции, это прекрасно работает, когда аргументом является одна запись, но при попытке передать фрагмент записей яне могу понять мой путь через указатели.

package temp

import (
    "encoding/json"

    uuid "github.com/satori/go.uuid"
)

type BaseEntry struct {
    ID uuid.UUID
}

func (entry *BaseEntry) GetID() uuid.UUID {
    return entry.ID
}

type Entry interface {
    GetID() uuid.UUID
}

func GetByCollection(collectionName string, entries []Entry) {
    // This could be a lookup in a database or filesystem

    collections := map[string][]string{
        "books": []string{
            `{"ID": "c8cc718d-94a9-4605-a4bb-05d5aa067fd6", "Title": "Nils Holgersson", "Author": "d89e4a0a-6a65-4754-9090-f8b6c7d6eeec"}`,
            `{"ID": "46ad379e-17f2-4961-b615-d4301160f892", "Title": "Ronja rövardotter", "Author": "a1bba636-0700-474f-8fe8-2ad3ec9d061c"}`,
        },
        "authors": []string{
            `{"ID": "d89e4a0a-6a65-4754-9090-f8b6c7d6eeec", "Name": "Selma Lagerlöf"}`,
            `{"ID": "a1bba636-0700-474f-8fe8-2ad3ec9d061c", "Name": "Astrid Lindgren"}`,
        },
    }

    if collections[collectionName] != nil {
        for _, entryData := range collections[collectionName] {
            entry := BaseEntry{}
            json.Unmarshal([]byte(entryData), &entry)
            *entries = append(*entries, &entry)
        }
    }
}

И тесты, в которых я пытаюсь использовать это:

    package temp_test

    import (
        "testing"

        "github.com/mojlighetsministeriet/next/temp"
        uuid "github.com/satori/go.uuid"
        "github.com/stretchr/testify/assert"
    )

    type Book struct {
        temp.BaseEntry
    }

    func TestPopulate(test *testing.T) {
        books := []Book{}
        temp.GetByCollection("books", &books)
        assert.NotEqual(test, uuid.Nil, books[0].GetID())
    }

В зависимости от того, объявлю ли я GetByCollection(collectionName string, entries []Entry) или GetByCollection(collectionName string, entries *[]Entry), я получу либо:

# github.com/mojlighetsministeriet/next/temp
./main.go:39:4: invalid indirect of entries (type []Entry)

или

# github.com/mojlighetsministeriet/next/temp_test
./main_test.go:17:32: cannot use &books (type *[]Book) as type * 
[]temp.Entry in argument to temp.GetByCollection

Как мне написать, чтобы я мог выбрать тип записи, вызвав GetByCollection, например, срезом [] Book или [] Author, который заполняется?

Мне, вероятно, нужно как-то рефакторинг, в конце концов мне нравится что-то похожее на метод запроса GORM (http://gorm.io/docs/query.html#Query) db.Find(&users), но вроде GetByCollection("books", &books)

1 Ответ

0 голосов
/ 10 июня 2018

Отражение требуется для работы с различными типами срезов.Вот как это сделать:

func GetByCollection(collectionName string, result interface{}) {    
    collections := map[string][]string{
        "books": []string{
            `{"ID": "c8cc718d-94a9-4605-a4bb-05d5aa067fd6", "Title": "Nils Holgersson", "Author": "d89e4a0a-6a65-4754-9090-f8b6c7d6eeec"}`,
            `{"ID": "46ad379e-17f2-4961-b615-d4301160f892", "Title": "Ronja rövardotter", "Author": "a1bba636-0700-474f-8fe8-2ad3ec9d061c"}`,
        },
        "authors": []string{
            `{"ID": "d89e4a0a-6a65-4754-9090-f8b6c7d6eeec", "Name": "Selma Lagerlöf"}`,
            `{"ID": "a1bba636-0700-474f-8fe8-2ad3ec9d061c", "Name": "Astrid Lindgren"}`,
        },
    }

    slice := reflect.ValueOf(result).Elem()
    elementType := slice.Type().Elem()

    for _, entryData := range collections[collectionName] {
        v := reflect.New(elementType)
        json.Unmarshal([]byte(entryData), v.Interface())
        slice.Set(reflect.Append(slice, v.Elem()))
    }
}

Назовите это так:

var books []Book
GetByCollection("books", &books)

Пример игровой площадки

...