Golang generic return type on a method - go

I'm new to golang, and I'm struggling to see how I can get some generic behavior on a method.
func (s *service) getCars(filter Filter) ([]car, error){
var err error
var cars []car
switch filter {
case filter.Toyota:
cars, err = s.dbClient.getToyotas()
case filter.Honda:
cars, err = s.dbClient.getHondas()
}
return cars, nil
}
The objects returned from s.dbClient.getToyotas() and s.dbClient.getHondas() are data structs with the same fields, but have different types. I should note these returned structs are auto generated, so I don't have the ability to alter them.
It looks like go interfaces only allow methods, and not data fields, so I don't if it's even possible to define a car type that can be used as the return type. I looked at generics as well, but it seems that generics are not allowed on struct methods.
What's the idiomatic way of doing something like this in go?

Defining a common interface is the idiomatic way to go.
Access to fields can be "modeled" by defining accessor methods on interface and then implementing those on specific structs.
In fact, it is a better solution as those methods can do more than simple access to internal, private variables. For example, they can have lazy initialization or cached access built in. Each struct can have unique implementation or you can have a base struct with default one.

Related

Why can't I assign a embedded struct to a parent struct in go?

I have below code try to assign embed struct to its parent struct. There are two set of structure: Guider is the parent struct, DataBlock extends from it. The method func Put(x Guider) accept a parameter with type Guider. It works when I pass a DataBlock variable.
However, the other case is Mock extends from zerolog.Event, but it fails to pass the parameter on the method Test(e zerolog.Event)
I got the following error:
cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
Why are these two cases works differently? How can I make them both work?
package main
import (
"fmt"
"github.com/rs/zerolog"
)
type Guider interface {
Guid() string
}
type FSEntity struct {
guid string
}
func (e FSEntity) Guid() string {
return e.guid
}
func Put(x Guider) {
fmt.Printf("%+v\n", x)
}
type Mock struct {
zerolog.Event
}
func Test(e zerolog.Event) {
}
//Child struct:
type DataBlock struct {
FSEntity
data []byte
}
func main() {
myVar := DataBlock{}
myVar.guid = "test"
myVar.data = []byte("moar test")
Put(myVar) // it works
m := Mock{}
Test(m) // it doesn't work. cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
}
First, a couple of definitions:
Polymorphism
Polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.
Subtyping
Subtyping (also subtype polymorphism or inclusion polymorphism) is a form of type polymorphism in which a subtype is a datatype that is related to another datatype (the supertype) by some notion of substitutability, meaning that program elements, typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype
Inheritance
In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation.
Object composition
Object composition and object aggregation are closely related ways to combine objects or data types into more complex ones.
Golang follows composition over inheritance principle, e.g. it doesn't support inheritance. So when you're saying
Mock extends from zerolog.Event
you actually mean that Mock includes zerolog.Event struct.
The way Golang implements polymorphism is interface. All types that implement some interface can be used in its place. It's what you see when use Guider.
However, it doesn't work for simple structs. zerolog.Event is a struct inside Mock.
So, normally, Test function should accept some interface as a parameter, and both mock and real event should implement this interface. However, it looks like zerolog doesn't provide interface for Event. So instead you should access the Event field of you struct. Example
Put(myVar) is legal because myVar is a DataBlock which contains (not inherits from and not implements) an FSEntity which in turn implements the Guider interface.
Since Put accepts a Guider, the reference to myVar is compatible, by virtue of the anonymous FSEntity field it contains which implements Guider. The implementation of Guider on FSEntity is (in effect) elevated to the containing struct (providing a means of delegating interfaces). This only occurs if the contained field is anonymous.
But in the case of Test(m), the function accepts a zerolog.Event which is a struct type, not an interface. As such, there is no "delegation" possible. Test() must be passed a zerolog.Event and in this scenario, this requires that you use the type name of the anonymous field:
Type(m.Event)
Some bonus info:
If DataBlock contained two anonymous fields which both implemented Guider then implicit delegation/elevation cannot take place; golang does not know which of the contained implementations should be delegated to/elevated (if any). In that scenario you must again use the name of the field that you wish to pass to the Put() function:
// given...
type Foo string
func (f Foo) Guid() string {
return string(f)
}
// and...
type DataBlock struct {
FSEntity
Foo
data []byte
}
// then...
Put(myVar) // is now illegal
// and must instead use either/or:
Put(myVar.FSEntity)
Put(myVar.Foo)
Whether implicit or explicit, the crucial distinction is that it is a field of the DataBlock (myVar) that is passed to Put(), not myVar itself.
If you want to pass the DataBlock to Put(), using a Guider interface, then DataBlock must itself implement the Guider interface.
Take this with a grain of salt, since I'm not familiar with zerolog package.
Your Guider is an interface, which might have any underlying type as long as Guid() method is satisfied. I assume this is happening through DataBlock containing FSEntity, which itself implements Guid() method, therefore satisfies MIGHT the interface.
On the other hand, I don't know what methods should be implemented to satisfy zerolog.Event or if it's even an interface, or a struct straight up. If it's an interface, you might need to implement it's required methods to be able to use DataBlock as zerolog.Event type. You might want/need to dig into that direction for some very specific answers.

Polymorphism on structs without methods in Go

I'm working on several web server projects in Go, and there is a common problem that I'm always facing. I know we can achieve something like polymorphism in Go with interfaces and methods, but many times I had a scenario that I needed polymorphism on some data-holder structs that (maybe) just had some common fields, and no methods at all.
For example consider a story writing platform, where each user can write short stories and novels:
type ShortStory struct {
Name string
ID int
Body string
}
type LongStory struct {
Name string
ID int
Chapters []string
}
Now I simply want to have a data layer function, say GetStories(), which fetches all stories written by a user from database.
func GetStories(id int) []SOME_TYPE {
...
}
There are really no methods that I want to have on my ShortStory and LongStory structs. I know I can add a dummy method and let them satisfy some Storier interface, then use that interface as return type. But since there is no method I would want on a data container model, adding a dummy method just for the language to enable a feature, seems like a poor design choice to me.
I can also make the function return []interface{}, but that's against the whole idea of "typed language" I believe.
Another way is to have two separate GetShortStories() and GetLongStories() methods, which return a slice of their own type. But at some point I would finally want to merge those two slices into one and there I would again need a []interface{}. Yes, I can return a JSON like:
{
"short_stories" : [...],
"long_stories" : [...]
}
But I want my json to be like:
[{...}, {...}, {...}]
And I wouldn't change my APIs because of a language's limits!
I'm not a pro in Go, so am I missing something here? Is there a Go-ish approach to this, or is it really bad language design on Golang's side?
If you cannot express what you want to do using the features of a language, you should first try to change the way you structure your program before blaming the language itself. There are concepts that cannot be expressed in Go but can be expressed well in other languages, and there are concepts you cannot express well in other languages but you can in Go. Change the way you solve the problem to effectively use the language.
One way you can address your problem is using a different type of struct:
type Story struct {
Name string
ID int
ShortBody string
Chapters []string
}
If the Chapters is empty, then it is a short story.
Another way:
type Story struct {
Name string
ID int
Content StoryContent
}
type StoryContent interface {
Type() string
}
type ShortStory interface {
StoryContent
Body() string
}
type LongStory interface {
StoryContent
Chapters() []string
}
etc.

Access all fields from parent method

I'm developing an application where data is stored in mongodb. There are several collections and of course all of them have some common fields (like Id, creation date, etc) and methods (for example Insert). In my vision, I need to create base model struct with needed fields and methods, and then embed this struct into my models. Unfortunately, this doesn't work because method defined for base model doesn't see child fields.
I don't know how to explain further. Here is code in playground:
https://play.golang.org/p/_x-B78g4TV
It uses json instead of mgo, but idea is still the same.
I want the output to be:
Saving to 'my_model_collection'
{"_id":42, "foo": "Some value for foo", "bar": "Here we set some value for bar"}
Not:
Saving to 'my_model_collection'
{"_id":42}
Writing that insert method for each my model seems to be against DRY, so what is correct/idiomatic way to achieve this in Go?
This is not possible, for details see my answer: Can embedded struct method have knowledge of parent/child?
You may do 2 things:
1. Abandon method and make it a helper / utility function
The idea is to make Insert() detached from BaseModel and make it a simple function, and you pass the document to it which you want to save.
I personally prefer this option, as it requires less hassle and maintenance. It could look like this:
func Insert(doc interface{}) {
j, _ := json.Marshal(doc)
fmt.Println(string(j))
}
You also had a "typo" in the tags:
type MyModel struct {
*BaseModel
Foo string `json:"foo"`
Bar string `json:"bar"`
}
Using it:
Insert(m)
Output (try it on the Go Playground):
{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}
2. Pass the (pointer to) the wrapper to the BaseModel
In this approach, you have to pass a pointer to the embedder struct so the BaseModel.Insert() method will have a pointer to it, and may use that to save / marshal. This is basically manually maintaining a "reference" to the struct that embeds us and is being saved/marshalled.
This is how it could look like:
type BaseModel struct {
Id int `json:"_id"`
collectionName string
wrapper interface{}
}
And then in the Insert() method save the wrapper:
func (m *BaseModel) Insert() {
fmt.Printf("Saving to '%v'\n", m.collectionName)
j, _ := json.Marshal(m.wrapper)
fmt.Println(string(j))
}
Creation is slightly more complex:
func NewMyModel() *MyModel {
mm := &MyModel{
Foo: "Some value for foo",
}
mm.BaseModel = NewBaseModel("my_model_collection", mm)
return mm
}
But output is as you wish:
Saving to 'my_model_collection'
{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}
Try it on the Go Playground.
In Golang, you can't override a parent method, because that's not how polymorphism works. The Insert method will apply on the BaseModel member, and not on MyModel.
Also, you're trying to use mgo in an improper way. If you want to insert documents in collections, then you already have an Insert method for a Collection struct which works on interface{} types (same as json.Marshal).
Of course, you can have a BaseModel that will contain fields shared by all of your models. In fact, GORM uses a similar approach and provides a Model struct to be included in every child model.
Well known problem ;o) Member variables (like collectionName) which name starts with lower letter are not visible from other packages (like json). Therefore change struct to:
type BaseModel struct {
Id int `json:"_id"`
CollectionName string `json:"collectionName"`
}
and world will be better place to live in.

Use map[string]SpecificType with method of map[string]SomeInterface into

I get cannot use map[string]MyType literal (type map[string]MyType) as type map[string]IterableWithID in argument to MapToList with the code below, how do I pass in a concrete map type to method that expects a interface type?
https://play.golang.org/p/G7VzMwrRRw
Go's interface convention doesn't quite work the same way as in, say, Java (and the designers apparently didn't like the idea of getters and setters very much :-/ ). So you've got two core problems:
A map[string]Foo is not the same as a map[string]Bar, even if Bar implements Foo, so you have to break it out a bit (use make() beforehand, then assign in a single assignment).
Interface methods are called by value with no pointers, so you really need to do foo = foo.Method(bar) in your callers or get really pointer-happy to implement something like this.
What you can do to more-or-less simulate what you want:
type IterableWithID interface {
SetID(id string) IterableWithID // use as foo = foo.SetID(bar)
}
func (t MyType) SetID(id string) IterableWithID {
t.ID = id
return t
}
...and to deal with the typing problem
t := make(map[string]IterableWithID)
t["foo"] = MyType{}
MapToList(t) // This is a map[string]IterableWithID, so compiler's happy.
...and finally...
value = value.SetID(key) // We set back the copy of the value we mutated
The final value= deals with the fact that the method gets a fresh copy of the value object, so the original would be untouched by your method (the change would simply vanish).
Updated code on the Go Playground
...but it's not particularly idiomatic Go--they really want you to just reference struct members rather than use Java-style mutators in interfaces (though TBH I'm not so keen on that little detail--mutators are supes handy to do validation).
You can't do what you want to do because the two map types are different. It doesn't matter that the element type of one is a type that implements the interface which is the element type of the other. The map type that you pass into the function has to be map[string]IterableWithID. You could create a map of that type, assign values of type MyType to the map, and pass that to the function.
See https://play.golang.org/p/NfsTlunHkW
Also, you probably don't want to be returning a pointer to a slice in MapToList. Just return the slice itself. A slice contains a reference to the underlying array.

Any down-side always using pointers for struct field types?

Originally I figured I'd only use pointers for optional struct fields which could potentionally be nil in cases which it was initially built for.
As my code evolved I was writing different layers upon my models - for xml and json (un)marshalling. In these cases even the fields I thought would always be a requirement (Id, Name etc) actually turned out to be optional for some layers.
In the end I had put a * in front of all the fields including so int became *int, string became *string etc.
Now I'm wondering if I had been better of not generalising my code so much? I could have duplicated the code instead, which I find rather ugly - but perhaps more efficient than using pointers for all struct fields?
So my question is whether this is turning into an anti-pattern and just a bad habbit, or if this added flexibility does not come at a cost from a performance point of view?
Eg. can you come up with good arguments for sticking with option A:
type MyStruct struct {
Id int
Name string
ParentId *int
// etc.. only pointers where NULL columns in db might occur
}
over this option B:
type MyStruct struct {
Id *int
Name *string
ParentId *int
// etc... using *pointers for all fields
}
Would the best practice way of modelling your structs be from a purely database/column perspective, or eg if you had:
func (m *MyStruct) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var v struct {
XMLName xml.Name `xml:"myStruct"`
Name string `xml:"name"`
Parent string `xml:"parent"`
Children []*MyStruct `xml:"children,omitempty"`
}
err := d.DecodeElement(&v, &start)
if err != nil {
return err
}
m.Id = nil // adding to db from xml, there's initially no Id, until after the insert
m.Name = v.Name // a parent might be referenced by name or alias
m.ParentId = nil // not by parentId, since it's not created yet, but maybe by nesting elements like you see above in the V struct (Children []*ContentType)
// etc..
return nil
}
This example could be part of the scenario where you want to add elements from XML to the database. Here ids would generally not make sense, so instead we use nesting and references on name or other aliases. An Id for the structs would not be set until we got the id, after the INSERT query. Then using that ID we could traverse down the hierachy to the child elements etc.
This would allow us to have just 1 MyStruct, and use eg. different POST http request handler functions, depending if the call came from form input, or xml importing where a nested hierarchy and different relations might need come different handling.
In the end I guess what I'm asking is:
Would you be better off separating struct models for db, xml- and json operations (or whatever scenario that you can think of), than using struct field pointers all the way, so we can reuse the model for different, yet related stuff?
Apart from possible performance (more pointers = more things for the GC to scan), safety (nil pointer dereference), convenience (s.a = 2 vs s.a = new(int); *s.a = 42), and memory penalties (a bool is one byte, a *bool is four to eight), there is one thing that really bothers me in the all-pointer approach. It violates the Single responsibility principle.
Is the MyStruct you get from XML or DB same as MyStruct? What if the DB schema will change? What if the XML changes format? What if you'll also need to unmarshal it into JSON, but in a slightly different manner? And what if you need to support all that (and in multiple versions!) at the same time?
A lot of pain comes to you when you try to make one thing do many things. Is having one do-it-all type instead of N specialised types really worth it?

Resources