Chainable struct methods that satisfies multiple interfaces? - go

I have a need to create multiple structs with almost the same fields and methods with the same actions. Instead of doing that I thought, why not create a single struct and use interfaces to limit interactions. It worked great! Now the problem. I want the methods to be chainable. To do that, methods need to return a reference to the struct. After doing that, all the returns complain that (struct) does not implement <interface> (wrong type for <method> method). Which is expected.
The question is, is it possible to use a single struct with multiple interfaces that has chainable methods? Or is creating individual, duplicate, structs for every interface the only way?
I thought of using composition but I still need to define methods that will call the embedded struct methods, in which case there's no difference to creating new pure structs.
Example problem:
https://play.golang.org/p/JrsHATdi2dr
package main
import (
"fmt"
)
type A interface {
SetA(string) A
Done()
}
type B interface {
SetB(string) B
Done()
}
type t struct {
a string
b string
}
func (t *t) SetA(a string) *t { t.a = a; return t }
func (t *t) SetB(b string) *t { t.b = b; return t }
func (t *t) Done() { fmt.Println(t.a, t.b) }
func NewTA() A {
return &t{}
}
func NewTB() B {
return &t{}
}
func main() {
fmt.Println("Hello, playground")
ta := NewTA()
ta.SetA("a")
ta.Done()
tb := NewTB()
tb.SetB("b")
tb.Done()
}

When you use *t as return type in SetA and SetB that means t are not implement A and B interface. The signature of SetA and SetB function of *t doesn't match with interface A and B accordingly.
Accually SetA(a string) A and SetA(a string) *t are not same think. You used A as return type for SetA in interface but use *t as return type for t, go doesn't support this. Same for SetB function
If you do like this then it will work because now function signature matched
func (t *t) SetA(a string) A { t.a = a; return A(t) }
func (t *t) SetB(b string) B { t.b = b; return B(t) }
Code in go playground here

Related

refactor methods which use reflection

Suppose I have two struct which are
type A struct {
}
func (a *A) SomeFunc (s string) {
// some codes
}
func (a *A) ProcessRequest(funcName string, parameters map[string]*structpb.Value) error {
...
if method := reflect.ValueOf(a).MethodByName(funcName); method.IsValid()
...
values := make([]reflect.Value, 0)
...
rv := method.Call(values)
...
}
type B struct {
}
func (b *B) AnotherFunc (i int) {
//somecode
}
and I am using reflection to call methods in A which is SomeFunc and it is working well.
Now I need to write a struct B which also has ProcessRequest which has exacly same code with the one in A.
Is there anyway I can refactor this?
I have tried to create
type ProcessProvider struct{
}
and let A and B embed it but when the method is called, *ProcessProvider is passed as the receiver and it cannot find SomeFunc and AnotherFunc.
An interface
type ProcessProvider interface {
ProcessRequest(serviceName string, parameters map[string]*structpb.Value) error
}
is helpful but I still have to copy paste the code.

Go generics: self-referring interface constraint

I have a couple custom types that I need to process in the same way. It seems like a perfect use for generics. In the process, I need to call methods on instances of the types, and those methods return different instances of the same types, and then I need to call methods on those returned instances, which I can't get to work. For the purpose of this question, I've fabricated a much simpler set of types and a process that exemplifies the problem I'm running in to.
Here's a working example without generics that shows the types (Circle and Square), and a process (.Bigger().Smaller()) I'll be trying to abstract into a generic function later (online demo):
package main
import (
"fmt"
)
type Circle struct{ r float64 }
func NewCircle(r float64) *Circle { return &Circle{r: r} }
func (c *Circle) Radius() float64 { return c.r }
func (c *Circle) Bigger() *Circle { return &Circle{r: c.r + 1} }
func (c *Circle) Smaller() *Circle { return &Circle{r: c.r - 1} }
type Square struct{ s float64 }
func NewSquare(s float64) *Square { return &Square{s: s} }
func (s *Square) Side() float64 { return s.s }
func (s1 *Square) Bigger() *Square { return &Square{s: s1.s + 1} }
func (s1 *Square) Smaller() *Square { return &Square{s: s1.s - 1} }
func main() {
fmt.Println(NewCircle(3).Bigger().Smaller().Radius()) // prints 3
fmt.Println(NewSquare(6).Bigger().Smaller().Side()) // prints 6
}
The first thing I do to make a generic function is to define a type constraint:
type ShapeType interface {
*Circle | *Square
}
I'll be passing a ShapeType to a process method, and I need to be able to call methods on the ShapeType instance, so I need to define another type constraint which specifies the methods that can be called on a ShapeType:
type Shape[ST ShapeType] interface {
Bigger() ST
Smaller() ST
}
With these, I can write a process method (online demo):
func process[ST ShapeType](s Shape[ST]) ST {
return s.Bigger().Smaller()
}
This fails to compile however, as the return value of s.Bigger() is an ST, not a Shape[ST], so go doesn't know that it can then call Smaller() on the return value of s.Bigger(). In go's words:
s.Bigger().Smaller undefined (type ST has no field or method Smaller)
If Bigger() and Smaller() didn't return instances of their receiver types, I could write:
type Shape interface {
*Circle | *Square
Bigger()
Smaller()
}
func process[S Shape](x S) S {
x.Bigger().Smaller()
return x // I guess we wouldn't even have to return x, but just for example's sake
}
Instead I would need to write:
type Shape interface {
*Circle | *Square
Bigger() Shape
Smaller() Shape
}
and it appears go doesn't like self-referential type constraints.
If it were possible to assert/convert a concrete type to an interface it conforms to, then I could make it work, but it doesn't appear to be possible to do that (online demo):
func process[ST ShapeType](s Shape[ST]) ST {
s1 := s.Bigger()
s2 := s1.(Shape[ST]) // go is not happy here
return s2.Smaller()
}
For this, go says:
cannot use type assertion on type parameter value s1 (variable of type ST constrained by ShapeType)
I don't know what else to try.
Is it possible to work with these kinds of types with generics? If so, how?
Combine your two attempted interfaces together:
type Shape[ST any] interface {
*Circle | *Square
Bigger() ST
Smaller() ST
}
And then instantiate the constraint of process with the type parameter itself:
func process[ST Shape[ST]](s ST) ST {
return s.Bigger().Smaller()
}
Adding the union element *Circle | *Square into Shape[ST any] means that only those two types will be able to implement the interface
Then using the type parameter in the method signature, like Bigger() ST, means that whichever type is passed has a method that returns itself.
If you want to keep ShapeType as a separated interface, you can write Shape as:
type Shape[ST any] interface {
ShapeType
Bigger() ST
Smaller() ST
}
You can also use process method with type inference, without any issue:
func main() {
c1 := NewCircle(3)
c2 := process(c1)
fmt.Println(c2.Radius()) // prints 3 as expected
fmt.Printf("%T\n", c2) // *main.Circle
s1 := NewSquare(6)
s2 := process(s1)
fmt.Println(s2.Side()) // prints 6 as expected
fmt.Printf("%T\n", s2) // *main.Square
}
Final playground: https://go.dev/play/p/_mR4wkxXupH

Struct does not implement interface if it has a method whose parameter implements interface

I have a package in which I have two interfaces
package main
type A interface {
Close()
}
type B interface {
Connect() (A, error)
}
I have also two structures which implements these interfaces
type C struct {
}
func (c *C) Close() {
}
type D struct {
}
func (d *D) Connect() (*C, error) {
c := new(C)
return c, nil
}
Next I have a function which as a parameter wants an object which implements interface B
func test(b B) {
}
Finally, at the main() function I create D structure object and want to call test() function
func main() {
d := new(D)
test(d)
}
If I try to build that package I have an error.
cannot use d (type *D) as type B in argument to test:
*D does not implement B (wrong type for Connect method)
have Connect() (*C, error)
want Connect() (A, error)
It is simple example of my code where I use external package and want to mock structures for tests. Is it any solution to use interfaces instead of types?
For implementing the interface there is one to concern about which is:
A Go type satisfies an interface by implementing the methods of that
interface, nothing more. This property allows interfaces to be defined
and used without having to modify existing code. It enables a kind of
structural typing that promotes separation of concerns and improves
code re-use, and makes it easier to build on patterns that emerge as
the code develops.
The error you are getting because the struct D you are using as an argument to test function does not implement the interface. The reason behind this is that the function Connect you are using with receiver D is different. Since it has different return type:
func (d *D) Connect() (*C, error) { // the struct D does not implement the interface B because of wrong function definition to interface B function
c := new(C)
return c, nil
}
while if you want to implement the interface B the function definition along with its return type should match the function in interface B which is
type B interface {
Connect() (A, error)
}
So if you want to implement the interface the Connect method you are using should match the Connect method of the interface B
package main
type A interface {
Close()
}
type B interface {
Connect() (A, error)
}
type C struct {
}
func (c *C) Close() {
}
type D struct {
}
func (d *D) Connect() (A, error) {
c := new(C)
return c, nil
}
func test(b B) {}
func main() {
d := new(D)
test(d)
}
Check on Go Playground
Consider this simple interface to represent an object that can compare itself with another value:
type Equaler interface {
Equal(Equaler) bool
}
and this type, T:
type T int
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler
The argument type of T.Equal is T, not literally the required type Equaler.
In Go, the type system does not promote the argument of Equal; that is the programmer's responsibility, as illustrated by the type T2, which does implement Equaler:
type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) } // satisfies Equaler
The returned type for your Connect method should be A and not *C.
The way you defined the Connect method is that it should return an interface, not a specific type. You will still be able to return *C as it implements the A interface.
package main
type A interface {
Close()
}
type B interface {
Connect() (A, error)
}
type C struct {
}
func (c *C) Close() {
}
type D struct {
}
func (d *D) Connect() (A, error) {
c := new(C)
println("successfully created new C:", c)
return c, nil
}
func test(b B) {
b.Connect()
}
func main() {
d := new(D)
test(d)
}
Outputs
successfully created new C: 0xe28f0
Try it out yourself here

Effectively wrapping public sdk types in golang

I am using pagerduty go sdk to do a bunch of api requests.
Particularly I am making use of
func NewClient(authToken string) *Client
to create a new Client type. I want to add some utility functions to my own work to *Client. I tried doing this:
type BetterPdClient *pagerduty.Client
func NewClient(auth string) BetterPdClient {
return pagerduty.NewClient(auth)
}
func (b *BetterPdClient) DoSomething() {
b.GetIncident(....)
}
func main() {
pd_client := NewClient("")
fmt.Println(pd_client)
pd_client.DoSomething()
}
But I get the following error:
invalid receiver type *BetterPdClient (BetterPdClient is a pointer type)
I understand that DoSomething() is expecting a pointer as caller. Only other way I could think of is sending the ptr as a function argument:
func NewClient(auth string) *pagerduty.Client {
return pagerduty.NewClient(auth)
}
func DoSomething(cl *pagerduty.Client) {
fmt.Println(cl)
}
func main() {
pd_client := NewClient("")
fmt.Println(pd_client)
DoSomething(pd_client)
}
Is there a better way?
Declaring a type as a pointer to another type is almost never what you want because Go doesn't allow you to add methods to that new type, nor to the pointer of that type as you've already figured out yourself. This one doesn't compile either:
type T struct{}
type P *T
func (P) M() {}
If you want to "extend" a type without "hiding" it's existing functionality your best bet is to embed it in a struct.
type T struct{
// ...
}
func (T) M() {}
type U struct {
*T
}
func NewU() *U {
return &U{&T{}}
}
func (U) N() {}
func main() {
u := NewU()
u.M()
u.N()
}
And what I mean by "hiding existing functionality" is that when you define a new type in terms of another, already existing type, your new type will not get direct access to the methods of the existing type. All you're doing is just saying that your new type should have the same structure as the already existing type. Although it's worth pointing out that this property gives you the ability to convert one type to the other...
type T struct{
// ...
}
func (T) M() {}
type U T
func NewU() *U {
return &U{}
}
func (U) N() {}
func main() {
u := NewU()
u.M() // compile error
u.N()
// convert *U to *T and then call M
(*T)(u).M()
}

How can I store types in go maps for initialisation at a later stage

I'm trying to implement a factory function that will return an instance of one of many structs that fulfil the contract of an interface X.
m := make(map[string] ?)
func init () {
m["a"] = ?
m["b"] = ?
}
type X interface {
y()
}
type A struct {}
func (a * A) y () {}
type B struct {}
func (b * B) y () {}
function factory(name string) X {
return &m[name]{}
}
The code above is just a simplified demonstration of what I'm trying to achieve - looking for pointers for whether this is possible, or if there is a different go idiom to solve this kind of requirement that I'm missing.
You can use map[string]X, with X the interface (which can reference a value or a pointer of any object respecting X contract)
or if there is a different go idiom to solve this kind of requirement that I'm missing?
You can also use reflection (as in "Instance new Type") to implement your factory.
reflect.New(yourtype).Elem().Interface()
You can see a factory example in "is there a way to create an instance of a struct from a string?".
The quicker approach for a factory method (returning a new instance each time) is using a switch (like in this example):
// Create a new Widget interface based on WidgetType and set WidgetInfo
func New(wt WidgetType, wi WidgetInfo) Widget_iface {
switch wt {
case Widget_A:
return newWidgetA(wi)
case Widget_B:
return newWidgetB(wi)
}
return nil
}
If you have a simple value type then as #VonC said, you can just use map[string]X and return a copy of the exemplar value.
For anything else, instead of using reflection, I'd just use a map of creation functions. Like what the image package does with
image.RegisterFormat.
E.g. (playground):
package main
import "fmt"
type X interface {
y()
}
type newXFunc func() X
// Here just a map variable and two functions, but
// this could be a type with two methods instead.
var m = map[string]newXFunc{}
func register(name string, fn newXFunc) {
m[name] = fn
}
func factory(name string) X {
return m[name]()
}
func init() {
// If register is exported than these
// calls can be in other packages that
// implement A and B.
register("a", NewA)
// For simple things that don't have/need
// their own stand-alone "new" function.
register("b", func() X { return B{} })
}
type A struct{}
func (a *A) y() {}
func NewA() X { return &A{} }
type B struct{}
func (b B) y() {}
func main() {
a1 := factory("a")
b1 := factory("b")
fmt.Printf("%T\n", a1)
fmt.Printf("%T\n", b1)
}

Resources