Golang: How to embed interface with different type parameters? - go

The code gives me error: DB redeclared.
Is there any idiomatic way to solve it? Or any work-around?
TIA
type a struct {
DB[int64]
DB[string]
}
type b interface {
DB[int64]
DB[string]
}
type DB[T any] interface {
GetList(query string) ([]T, error)
}

You can't embed the same interface, even with different type parameters. Regardless of how it is instantiated, you are trying to promote into the b interface two methods with the same name GetList and different signatures — given by the different instantiations of DB.
The situation is similar, although not technically the same, for embedding into a struct. In structs, the name of the embedded field is the name of the type — DB —, and a struct can't have two non-blank fields with the same name.
About how to solve this issue, it depends what you want to accomplish.
If you want to convey that "a implements DB with either type parameter" you can embed DB[T] and make a itself generic, and restrict a's type parameters:
type a[T int64 | string] struct {
DB[T]
}
// illustrative implementation of the DB[T] interface
// if you embed DB[T] you likely won't use `a` itself as receiver
func (r *a[T]) GetList(query string) ([]T, error) {
// also generic method
}
This is okay because DB's type parameter is constrained to any, and a's type parameter is more restrictive. This allows you to use a in other generic methods, or choose a specific type upon instantiation, but the implementation of GetList has to be parametrized too.
Otherwise if you need a to have separated methods that return int64 or string, you must give it different names.
Finally, you can embed instances of DB into two interfaces with different names, and then embed those into a instead.
type a struct {
DBStr
DBInt
}
type DBStr interface {
DB[string]
}
type DBInt interface {
DB[int64]
}
This way though the top-level selector isn't available because the method names are still the same. The program compiles, but you'll have to explicitly choose which field to call the method on:
myA := a{ /* init the two fields */ }
res, err = myA.DBStr.GetList(query)
// res is type []string
// or
res, err = myA.DBInt.GetList(query)
// res is type []int64

Related

How to modify a field in a struct of an unknown type?

I have multiple structs that have one common field; let's call it common here
type Struct1 struct {
foo string
bar string
common string
}
type Struct2 struct {
baz int
qux string
common string
}
I want to create a function that takes an Interface as input and nullifies common. The available struct types won't be known at compile time, so I can't create a separate function for each type, and I can't use a switch statement.
P.S: In my use-case, I want to nullify common because it holds the creation time of each struct, and I want to track the history of the struct, so I will know if it changes. Having the creation time inside the struct will mess this up because the creation time will be different every time a new struct is generated even though its actual data may be the same.
Define a struct with the common fields and make it implement an interface which says that it is able to nullify the common fields. Then embed this struct into your other struct types that should be able to nullify the fields.
// CommonNullifier is able to nullify its common field(s)
type CommonNullifier interface {
NullifyCommon()
}
// StructCommon contains the common struct fields
type StructCommon struct {
Common string
}
func (sc *StructCommon) NullifyCommon() {
sc.Common = ""
}
// Struct1 embeds common fields, thus implements CommonNullifier
type Struct1 struct {
StructCommon
Foo string
}
// Struct2 also embeds common fields, thus also implements CommonNullifier
type Struct2 struct {
StructCommon
Bar string
}
// NullifyCommon nullfies the 'common' fields in the argument
func NullifyCommon(s CommonNullifier) {
s.NullifyCommon()
}
(See it on the Go Playground)
You could also use reflection, but using an interface is generally more readable.

How GoLang's typecast to interface implemented by a struct and embedded struct works

I recently came across a code that is doing something I don't understand.
There are multiple structs having the same embedded struct and an interface that defines methods returning pointer to each struct. This interface is implemented by the embedded struct but only 'partially' by the individual structs, as such, each struct only implements the method where the pointer to that struct is returned.
For better understanding, here is the representative code:
type BarStocks interface {
GetVodka() *Vodka
GetMartini() *Martini
GetBourbon() *Bourbon
GetNegroni() *Negroni
GetManhattan() *Manhattan
}
type BaseAttributes struct {
ID uuid.UUID
Quantity float64
CreatedAt time.Time
UpdatedAt time.Time
}
func (e *BaseAttributes) GetVodka() *Vodka {
return nil
}
func (e *BaseAttributes) GetMartini() *Martini {
return nil
}
func (e *BaseAttributes) GetBourbon() *Bourbon {
return nil
}
func (e *BaseAttributes) GetNegroni() *Negroni {
return nil
}
func (e *BaseAttributes) GetManhattan() *Manhattan {
return nil
}
And then each individual struct implements only the method where its pointer is returned, for example:
type Vodka struct {
BaseAttributes
Label string
}
func (v *Vodka) GetVodka() *Vodka {
return v
}
Now in the code, this setup is used to typecast the individual struct to the interface as a pointer, something like this:
func someFunc() BarStocks {
v := Vodka{}
return &v
}
Now I am not too deep into Go yet and so unable to comprehend how the pointer to the struct becomes the same type as the interface.
Thanks in advance for any insight into this.
I'll do my best to answer the question I think you're asking.
The documentation on embedding explains the behavior you're seeing,
There's an important way in which embedding differs from subclassing.
When we embed a type, the methods of that type become methods of the
outer type, but when they are invoked the receiver of the method is
the inner type, not the outer one.
This explains how a Vodka struct, which embeds struct BaseAttributes which implements all of the methods in BarStocks is able to satisfy the interface Barstocks. This excerpt, however, does not explain how we effectively override GetVodka() for our Vodka struct.
To understand this we need to read another excerpt from the documentation.
Embedding types introduces the problem of name conflicts but the rules
to resolve them are simple. First, a field or method X hides any other
item X in a more deeply nested part of the type.
This excerpt explains that If Vodka implements GetVodka() and embeds a struct (BaseAttributes) which also implements GetVodka(), the outer-most definition is the one that takes precedence.
The combination of these behaviors explain how Vodka satisfies the BarStocks interface and has the behavior you see in the example code.
The additional methods missing from Vodka, but present on BarStocks are implemented for BaseAttributes and if the method is missing from Vodka, the method from the embedded strut will be called instead.
Note, methods in an embedded struct will not have access to the parent struct members. Also note, if you wanted to call the embedded struct's method when it has been defined in the parent (as GetVodka is for Vodka) then you would prepend the embedded type's name to the method. For example
myDrink := someFunc()
myDrink.BaseAttributes.GetVodka()
This is obviously not helpful in this case as all of the BaseAttribute methods return nil, but may be helpful in the future.

golang initializing struct with embedded template: too few values in struct initializer

I'm trying to initialize a golang struct with an embedded template. Since templates have no fields, I would expect that assigning the correct number of variables to a constructor would work, but instead the compiler complains that
main.go:17:19: too few values in struct initializer
package main
import "fmt"
type TestTemplate interface {
Name() string
}
type TestBase struct {
name string
TestTemplate
}
func New(name string) *TestBase {
return &TestBase{name} // This fails
//return &TestBase{name: name} // This works
}
func (v *TestBase) Name() string {
return v.name
}
func main() {
fmt.Println(New("Hello"))
}
https://golang.org/ref/spec#Struct_types
An embedded field is still a field, the name of which is derived from its type, therefore TestBase has actually two fields and not one, namely name and TestTemplate.
This compiles just fine:
var t *TestBase
t.TestTemplate.Print()
So when initializing TestBase you either specify the field names or you initialize all fields.
These all compile:
_ = &TestBase{name, nil}
_ = &TestBase{name: name}
_ = &TestBase{name: name, TestTemplate: nil}
_ = &TestBase{TestTemplate: nil}
It looks like (as far as general concepts go) you're confusing interfaces with composition (which is kind of how Go approaches the whole inheritance question.
This post might be helpful for you: https://medium.com/#gianbiondi/interfaces-in-go-59c3dc9c2d98
So TestTemplate is an interface.
That means that the struct TestBase will implement the methods (whose signature is) defined in the interface.
You should implement Print for TestBase.
But in anycase the error you're getting is because when you initialize a struct with no field names specified, it expects all the field names to be entered, see
https://gobyexample.com/structs
So remove the composition TestTemplate from the struct (and implement the method defined in the interface instead), and it should work.
Also, FYI, the Stringer interface with String method is what fmt.Println expects to print an arbitrary struct (not a Print method) see: https://tour.golang.org/methods/17

exported field in unexported struct

Example:
type myType struct {
foo []float64
Name string
}
myType is not exported, but Name field in it is exported.
Does this make sense to do this? Is that considered a bad practice?
I have something like this, and it compiles fine.
I can access the Name field if I create an exported array of myType:
var MyArray []myType = {... some initialization }
fmt.Println(MyArray[0].Name) // Name is visible and it compiles
It is perfectly valid to have unexported structs with exported fields. If the type is declared in another package, the declaration var MyArray []myType would be a compile-time error.
While it is perfectly valid to have an exported function with an unexported return type, it is usually annoying to use. The golint tool also gives a warning for such cases:
exported func XXX returns unexported type pname.tname, which can be annoying to use
In such cases it's better to also export the type; or if you can't or don't want to do that, then create an exported interface and the exported function should have a return type of that interface, and so the implementing type may remain unexported. Since interfaces cannot have fields (only methods), this may require you to add some getter methods.
Also note that in some cases this is exactly what you want: unexported struct with exported fields. Sometimes you want to pass the struct value to some other package for processing, and in order for the other package to be able to access the fields, they must be exported (but not the struct type itself).
Good example is when you want to generate a JSON response. You may create an unexported struct, and to be able to use the encoding/json package, the fields must be exported. For example:
type response struct {
Success bool `json:"success"`
Message string `json:"message"`
Data string `json:"data"`
}
func myHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp := &response{
Success: true,
Message: "OK",
Data: "some data",
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
// Handle err
}
}

GO Multiple Pointers

I'm trying to create a function that receives multiple types of struct and add those pointer values to another function.
Example:
type Model1 struct {
Name string
}
type Model2 struct {
Type bool
}
func MyFunc(value ...interface{}) {
OtherFunc(value...)
}
func main() {
MyFunc( new(Model), new(Mode2) );
}
The problem is that OtherFunc only allow &value, &value, etc as parameter. Have some way to pass those values like OtherFunc(&value...)?
I'm not sure this will solve your problem entirely however, the exact thing you requested is a feature in the language. You just have to use composite-literal syntax for instantiation instead of new. So you could do this to pass pointers; MyFunc( &Model{}, &Mode2{} )
Thing is, you're still going to be dealing with an interface{} within MyFunc so I'm not sure that will just be able to call OtherFunc without some unboxing (would probably be a type assertion if you want to get technical).

Resources