plakje struct != stukje interface dat het implementeert?

Ik heb een interface Model, die is geïmplementeerd door struct Person.

Om een ​​modelinstantie te krijgen, heb ik de volgende hulpfuncties:

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

Met de bovenstaande aanpak kan ik een correct getypte Person-instantie retourneren (kan later gemakkelijk nieuwe modellen toevoegen met dezelfde aanpak).

Toen ik iets soortgelijks probeerde te doen voor het retourneren van een deel van de modellen, krijg ik een foutmelding. Code:

func newModels(c string) []Model {
    switch c {
    case "person":
        return newPersons()
    }
    return nil
}
func newPersons() *[]Person {
    var models []Person
    return &models
}

Go klaagt met: cannot use newPersons() (type []Person) as type []Model in return argument

Mijn doel is om een ​​segment terug te sturen van welk modeltype dan ook dat wordt aangevraagd (of het nu gaat om []Person, []FutureModel, []Terminator2000, wij). Wat mis ik en hoe kan ik zo’n oplossing goed implementeren?


Antwoord 1, autoriteit 100%

Dit lijkt erg op een vraag die ik zojuist heb beantwoord: https://stackoverflow.com/a/12990540/727643

Het korte antwoord is dat je gelijk hebt. Een plakje structs is niet gelijk aan een plakje van een interface die de struct implementeert.

Een []Personen een []Modelhebben verschillende geheugenlay-outs. Dit komt omdat de typen waarvan ze plakjes zijn, verschillende geheugenlay-outs hebben. Een Modelis een interfacewaarde, wat betekent dat het in het geheugen twee woorden groot is. Het ene woord voor de typegegevens, het andere voor de gegevens. Een Personis een struct waarvan de grootte afhangt van de velden die het bevat. Om van een []Personnaar een []Modelte converteren, moet je de array doorlopen en een typeconversie uitvoeren voor elk element.

Aangezien deze conversie een O(n)-bewerking is en zou resulteren in het maken van een nieuwe slice, weigert Go dit impliciet te doen. Je kunt het expliciet doen met de volgende code.

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

En zoals dskinner opmerkte, wil je hoogstwaarschijnlijk een segment met verwijzingen en niet een verwijzing naar een segment. Een aanwijzer naar een segment is normaal gesproken niet nodig.

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

Antwoord 2, autoriteit 7%

Misschien is dit een probleem met uw retourtype *[]Person, waar het eigenlijk []*Personzou moeten zijn, dus om te verwijzen naar elke index van het segment een verwijzing naar een Person, en waar een slice []op zichzelf een verwijzing naar een array is.

Bekijk het volgende voorbeeld:

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())
}

Antwoord 3, autoriteit 6%

Omdat Stephen de vraag al heeft beantwoord en je een beginner bent, leg ik de nadruk op het geven van adviezen.

Een betere manier om met de interfaces van go te werken is om geen constructor terug te laten keren
de interface zoals je misschien gewend bent van andere talen, zoals java, maar om te hebben
een constructor voor elk object afzonderlijk, aangezien ze de interface impliciet implementeren.

In plaats van

newModel(type string) Model { ... }

je zou moeten doen

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

met Personen Politiciandie beide de methoden van Modelimplementeren.
U kunt nog steeds Personof Politiciangebruiken overal waar een Model
wordt geaccepteerd, maar u kunt ook andere interfaces implementeren.

Met uw methode bent u beperkt tot Modeltotdat u een handmatige conversie uitvoert naar
een ander type interface.

Stel dat ik een Personheb die de methode Walk()implementeert en een ModelShowOff()implementeert , zou het volgende niet direct werken:

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

Dit zou echter:

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

Antwoord 4, autoriteit 3%

Zoals anderen al hebben geantwoord, is []T een apart type. Ik zou alleen willen toevoegen dat een eenvoudig hulpprogramma kan worden gebruikt om ze generiek te converteren.

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
}

Nu kun je het als volgt gebruiken:

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

Antwoord 5, autoriteit 2%

Typen T en []T zijn verschillende typen en onderscheiden zijn ook hun methoden, zelfs als ze aan dezelfde interface voldoen. IOW, elk type dat aan het model voldoet, moet alle methoden van het model zelf implementeren – de methode-ontvanger kan slechts één specifiek type zijn.


Antwoord 6, autoriteit 2%

Zelfs als de implementatie van Go dit toestond, is het helaas ondeugdelijk: je kunt geen []Persontoewijzen aan een variabele van het type []Modelomdat een []Modelheeft verschillende mogelijkheden. Stel dat we bijvoorbeeld ook Animalhebben die Modelimplementeert:

var people []Person = ...
var models []Model = people // not allowed in real Go
models[0] = Animal{..} // ???
var person Person = people[0] // !!!

Als we regel 2 toestaan, dan zou regel 3 ook moeten werken omdat modelsperfect een Animalkunnen opslaan. En regel 4 zou nog steeds moeten werken omdat peoplePersons opslaat. Maar dan krijgen we een variabele van het type Personmet een Animal!

Java staat feitelijk het equivalent van regel 2 toe, en het wordt algemeen als een fout beschouwd. (De fout wordt tijdens runtime gedetecteerd; regel 3 zou een ArrayStoreExceptionopleveren.)

Other episodes