Golang:struct of slice!=它实现的接口切片?

时间:2012-10-21 03:26:10

标签: go

我有一个接口Model,它由struct Person实现。

要获取模型实例,我有以下辅助函数:

func newModel(c string) Model {
    switch c {
    case "person":
        return newPerson()
    }
    return nil
}

func newPerson() *Person {
    return &Person{}
}

上述方法允许我返回一个正确输入的Person实例(稍后可以使用相同的方法轻松添加新模型)。

当我尝试做类似的事情来返回一片模型时,我收到一个错误。代码:

func newModels(c string) []Model {
    switch c {
    case "person":
        return newPersons()
    }
    return nil
}

func newPersons() *[]Person {
    var models []Person
    return &models
}

抱怨:cannot use newPersons() (type []Person) as type []Model in return argument

我的目标是返回所请求的任何模型类型的片段([]Person[]FutureModel[]Terminator2000,w / e)。我缺少什么,以及如何正确实施这样的解决方案?

6 个答案:

答案 0 :(得分:81)

这与我刚才回答的问题非常相似:https://stackoverflow.com/a/12990540/727643

简短的回答是你是对的。一块结构不等于结构实现的接口的切片。

[]Person[]Model具有不同的内存布局。这是因为它们是切片的类型具有不同的内存布局。 Model是一个接口值,这意味着在内存中它的大小是两个单词。一个字为类型信息,另一个为数据。 Person是一个结构,其大小取决于它包含的字段。要从[]Person转换为[]Model,您需要遍历数组并为每个元素执行类型转换。

由于此转换是O(n)操作并且将导致创建新切片,因此Go拒绝隐式执行此操作。您可以使用以下代码明确地执行此操作。

models := make([]Model, len(persons))
for i, v := range persons {
    models[i] = Model(v)
}
return models

就像dskinner pointed out一样,你很可能想要一片指针,而不是指向切片的指针。通常不需要指向切片的指针。

*[]Person        // pointer to slice
[]*Person        // slice of pointers

答案 1 :(得分:8)

这可能是您的返回类型*[]Person的一个问题,它实际上应该是[]*Person,以便引用切片的每个索引是对Person的引用,以及切片[]本身就是对数组的引用。

查看以下示例:

package main

import (
    "fmt"
)

type Model interface {
    Name() string
}

type Person struct {}

func (p *Person) Name() string {
    return "Me"
}

func NewPersons() (models []*Person) {
    return models
}

func main() {
    var p Model
    p = new(Person)
    fmt.Println(p.Name())

    arr := NewPersons()
    arr = append(arr, new(Person))
    fmt.Println(arr[0].Name())
}

答案 2 :(得分:7)

斯蒂芬已经回答了这个问题并且你是初学者,我强调提出建议。

使用go的接口更好的方法是不要让构造函数返回 你可能习惯使用其他语言的接口,比如java,但要有 每个对象的构造函数,因为它们隐式实现了接口。

而不是

newModel(type string) Model { ... }

你应该做

newPerson() *Person { ... }
newPolitician() *Politician { ... }

PersonPolitician都实现了Model的方法。 您仍然可以在Person的任何地方使用PoliticianModel 被接受,但您也可以实现其他接口。

使用您的方法,在进行手动转换之前,您将被限制为Model 另一种接口类型。

假设我有Person实现方法Walk()Model实现ShowOff(),以下内容无法直接使用:

newModel("person").ShowOff()
newModel("person").Walk() // Does not compile, Model has no method Walk

然而,这会:

newPerson().ShowOff()
newPerson().Walk()

答案 3 :(得分:2)

类型T和[] T是不同的类型,它们的方法也是不同的,即使在满足相同的接口时也是如此。 IOW,每个满足模型的类型必须自己实现所有模型的方法 - 方法接收器只能是一种特定类型。

答案 4 :(得分:2)

正如其他人已经回答的那样,[] T是一种独特的类型。我想补充说,可以使用一个简单的实用程序来转换它们。

import "reflect"

// Convert a slice or array of a specific type to array of interface{}
func ToIntf(s interface{}) []interface{} {
    v := reflect.ValueOf(s)
    // There is no need to check, we want to panic if it's not slice or array
    intf := make([]interface{}, v.Len())
    for i := 0; i < v.Len(); i++ {
        intf[i] = v.Index(i).Interface()
    }
    return intf
}

现在,您可以像这样使用它:

ToIntf([]int{1,2,3})

答案 5 :(得分:0)

即使Go的实现允许这样做,也很遗憾:您不能将[]Person分配给类型[]Model的变量,因为[]Model具有不同的功能。例如,假设我们也有实现Animal的{​​{1}}:

Model

如果我们允许第二行,那么第三行也应该起作用,因为var people []Person = ... var models []Model = people // not allowed in real Go models[0] = Animal{..} // ??? var person Person = people[0] // !!! 可以很好地存储models。第4行仍然应该工作,因为Animal存储了people个。但是,最后我们得到一个Person类型的变量,其中包含一个Person

Java实际上允许与第二行相当,这被广泛认为是错误的。 (错误是在运行时捕获的;第3行会抛出Animal。)