首页 > 解决方案 > 如何填充作为函数引用传递的接口切片

问题描述

我有一个小例子,我尝试在函数中填充 []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)

标签: gostructtypesslice

解决方案


反射需要使用不同的切片类型。这是如何做到的:

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)

游乐场示例


推荐阅读