Auto cast subclass instance to interface - go

I have a interface, like this:
type Collections[T any] interface {
Add(element T) bool
Get(index uint64) T
Length() uint64
}
and I have a subclass implement the interface:
type LinkedList[T any] struct {
size uint64
head *linkedListNode[T]
tail *linkedListNode[T]
}
and I define a package function:
func IsEmpty[T any](collections Collections[T]) bool {
if collections == nil {
return true
}
return collections.Length() <= 0
}
now I have a question, if I have a constuctor like this:
func NewLinkedList[T any]() Collections[T] {
linkList := &LinkedList[T]{size: 0}
headNode := &linkedListNode[T]{}
tailNode := &linkedListNode[T]{}
headNode.next = tailNode
tailNode.pre = headNode
linkList.head = headNode
linkList.tail = tailNode
return linkList
}
then I can use the package function like this:
func TestLinkedListCreate(t *testing.T) {
var obj = datastruct.NewLinkedList[string]()
empty := datastruct.IsEmpty(obj)
println(empty)
}
but if I have a constuctor like this:
func NewLinkedList[T any]() *LinkedList[T] {
linkList := &LinkedList[T]{size: 0}
headNode := &linkedListNode[T]{}
tailNode := &linkedListNode[T]{}
headNode.next = tailNode
tailNode.pre = headNode
linkList.head = headNode
linkList.tail = tailNode
return linkList
}
I have to use package function like this:
func TestLinkedListCreate(t *testing.T) {
var obj datastruct.Collections[string] = datastruct.NewLinkedList[string]()
empty := datastruct.IsEmpty(obj)
println(empty)
}
so why golang can auto cast subclass to interface in function, but can't cast subclass to interface in declare?
I use golang 1.19

Volker said in a comment:
Go has no casts, no classes, no inheritance and no subclasses. Your question makes no sense. You cannot do traditional OOP in Go. Redesign

Related

How do I properly check whether a struct field implements an interface when it requires a pointer receiver?

I'm trying to determine if a field on a struct implements a given interface that has a pointer receiver.
I have a go playground setup with my confusion.
Say I have the following interface:
type Stringee interface {
FromString(s string) error
}
With an enum implementing it:
type Enum int
const (
EnumUnknown Enum = iota
EnumA
EnumB
)
func (my *Enum) String() string { return enumToStr[*my] }
func (my *Enum) FromString(s string) error {
if v, ok := enumFromStr[s]; ok {
*my = v
return nil
}
return errors.New("invalid value")
}
var enumToStr = map[Enum]string{
EnumA: "A",
EnumB: "B",
}
var enumFromStr = func() map[string]Enum {
m := make(map[string]Enum)
for k, v := range enumToStr {
m[v] = k
}
return m
}()
I first get the Type of the interface:
var stringeeType = reflect.TypeOf(new(Stringee)).Elem()
then I can perform checks against it:
e := EnumA
e.FromString("B")
e.String() // "B"
reflect.ValueOf(e).Type().Implements(stringeeType) // false
reflect.ValueOf(&e).Type().Implements(stringeeType) // true
Which is to be expected. However, what I don't know how to do is perform the check as a pointer if type is a field of a struct. For example:
Say I have the following struct:
type Struct struct {
E Enum
}
How do I check to see whether or not E implements the interface Stringee?
t := Struct{}
tv := reflect.ValueOf(&t)
fieldE := tv.Elem().Field(0)
fieldE.Type().Implements(stringeeType) // false
https://play.golang.org/p/eRUQ8EGo2-E
If the field is addressable, then take the address of the field and check that value:
if fieldE.CannAddr() {
impl := fieldE.Addr().Type().Implements(stringeeType)
fmt.Println("&T.E implements Stringee", impl)
}
Alternatively if you have only the reflect.Type not the relect.Value, for example when checking if a struct's field implements an interface, you can use reflect.PtrTo for this.
This func will check if any field of the given struct implements Stringee:
func CheckFields(s interface{}) {
rt := reflect.TypeOf(s)
for i := 0; i < rt.NumField(); i++ {
field := rt.Field(i)
ptr := reflect.PtrTo(field.Type)
if ptr.Implements(stringeeType) {
fmt.Println(field.Name + " implements Stringee")
}
}
}
type Test struct {
Field int
Field2 MyTypeWithStringee
}
test := Test{}
CheckFields(test)

Type that describes any go function

I want to write a function that partially applies a function to an argument, like this:
func partial(f AnyFuncType, arg interface{}) AnyFuncType {
return func(args ...interface{}) interface{} {
return f(arg, args)
}
}
type AnyFuncType func(args ...interface{}) interface{}
But that doesn't work even with the simplest function like that
func sum(a int, b int) int {
return a + b
}
func main() {
addToFive := partial(sum, 5)
}
because I get
./prog.go:16:23: cannot use sum (type func(int, int) int) as type AnyFuncType in argument to partial
compilation error. Now, I know that I could use interface{}, but is there a way to specify a more precise type for f that would work with any function?
You are trying to treat interface{} as a generic type, but interface{} is not a generic type and go will not match the signature of a function that takes interface{} as the signature of a function that takes a concrete type.
The problem is, subtyping in GO works only for interaces. Since AnyFuncType is not an interface, this won't work.
Use interface{} to represent a function of any type. There is not a more precise type that works with any function.
Use the reflect package to implement partial.
func partial(f interface{}, arg interface{}) interface{} {
v := reflect.ValueOf(f)
t := v.Type()
var in []reflect.Type
for i := 1; i < t.NumIn(); i++ {
in = append(in, t.In(i))
}
var out []reflect.Type
for i := 0; i < t.NumOut(); i++ {
out = append(out, t.Out(i))
}
var va reflect.Value
if arg != nil {
va = reflect.ValueOf(arg)
} else {
// Support `nil` as partial argument.
va = reflect.Zero(t.In(0))
}
return reflect.MakeFunc(reflect.FuncOf(in, out, t.IsVariadic()),
func(args []reflect.Value) []reflect.Value {
return v.Call(append([]reflect.Value{va}, args...))
}).Interface()
}
Use it like this:
addToFive := partial(sum, 5).(func(int) int)
fmt.Println(addToFive(1))
Run it on the playground.
I recommend using a closure to create partials instead of the partial function in this answer. The closure is more efficient and avoids tricky reflect code.
addToFive := func(x int) int { return sum(5, x) }
fmt.Println(addToFive(1))

Can a variable be used as a placeholder for a function call?

I am writing a program which has several structs and functions to handle these structs differently. I am having a generic function which calls the required function based on the inputs. Is there a generic way to use the returned value from getStruct()?
package main
var X func(s []string) A
var Y func(s []string) B
type A struct {
Name string
Place string
}
type B struct {
Name string
Place string
Value string
}
func newA(s []string) A {
a := A{
Name: s[0],
Place: s[1],
}
return a
}
func newB(s []string) B {
a := B{
Name: s[0],
Place: s[1],
Value: s[2],
}
return a
}
func getStruct(t string) interface{} {
switch {
case t == "A":
return X
case t == "B":
return Y
default:
return //someStruct
}
}
func main() {
buildNewStruct := getStruct("A") //Lets assume "A" here is got as an argument
var strSlice = []string{"Bob", "US"}
buildNewStruct(strSlice) //How to do this operation?
//I am hoping to use buildNewStruct(strSlice) to dynamically call
//either of newA(strSlice) or newB(strSlice) function
}
I have tried looking at this and this the later is not exactly the same as my question.
Since I am new to go, I am not sure if something like this is possible.
you can use the reflect package to set the struct properties to the equivalent index positioned value from an []interface{} slice.
package main
import (
"fmt"
"log"
"reflect"
)
func main() {
var a A
err := decode(&a, []interface{}{"Name", "Place"})
log.Println(err)
log.Println(a)
}
func decode(dst interface{}, values []interface{}) error {
rvptr := reflect.ValueOf(dst)
if rvptr.Kind() != reflect.Ptr {
return fmt.Errorf("value must be ptr")
}
rv := rvptr.Elem()
if rv.NumField() < len(values) {
return fmt.Errorf("too many values")
}
if rv.NumField() > len(values) {
return fmt.Errorf("too few values")
}
rvalues := reflect.ValueOf(values)
for i := range values {
f := rv.FieldByIndex([]int{i})
f.Set(rvalues.Index(i).Elem())
}
return nil
}
type A struct {
Name string
Place string
}
type B struct {
Name string
Place string
Value string
}
prints
$ go run main.go
2019/11/21 17:00:17 <nil>
2019/11/21 17:00:17 {Name Place}
The problem is the return type for the function.
func newA(in []string) interface{} {...}
func newB(in []string) interface{} {...}
func getStruct(name string) func([]string) interface{} {
switch name {
case "A": return newA
case "B": return newB
}
return nil
}
func main() {
buildNewStruct := getStruct("A")
var strSlice = []string{"Bob", "US"}
str:=buildNewStruct(strSlice)
if a, ok:=str.(A); ok {
...
}
}
With this approach, even though you saved some code by calling a unified buildNewStruct(), you have to use type assertions to figure out what is returned from that function, so this may not make a lot of sense. It depends on your exact use case though.

Using reflection to iterate over struct's struct members and calling a method on it

I have a struct that has one or more struct members. Each member is expected to implement a Validator interface.
I want to use reflection to iterate over all struct members and call the interface's Validate() method. For example:
package main
import "fmt"
import "reflect"
type Validator interface {
Validate()
}
type T1 struct {
S string
}
func (p *T1) Validate() {
fmt.Println("HERE 1")
}
type T2 struct {
S string
}
func (p *T2) Validate() {
fmt.Println("HERE 2")
}
type Top struct {
S1 T1
S2 T2
}
func main() {
var t Top
r := reflect.ValueOf(t)
for i := 0; i < r.NumField(); i++ {
f := r.Field(i)
if f.Kind() == reflect.Struct {
validator := f.Interface().(Validator)
validator.Validate()
}
}
}
When run, it outputs:
panic: interface conversion: main.T1 is not main.Validator: missing method Validate
If I change the Validate() methods to accept value (rather than pointer) receivers, then it works. However, I want to use pointer receivers since the structs may grow to be large.
How can I change the reflection code to work where the methods are defined taking pointer receivers?
I also tried using this line:
validator := f.Addr().Interface().(Validator)
to get a pointer, but it then outputs:
panic: reflect.Value.Addr of unaddressable value
None of your values are addressable, so you can't call any methods on them with pointer receivers.
If the S1 and S2 fields can't be pointers, you can still address them if you have a pointer to the Top struct:
r := reflect.ValueOf(&t).Elem()
for i := 0; i < r.NumField(); i++ {
f := r.Field(i)
if f.Kind() == reflect.Struct {
validator := f.Addr().Interface().(Validator)
validator.Validate()
}
}
"Top.S1" is of type "T1" and is different from "*T1" type defined "Validate()".
If you change "Top.S1" to "*T1" type and modify the field type checking, your code will work properly.
type Top struct {
S1 *T1
S2 *T2
}
func main() {
t := Top{&T1{}, &T2{}}
r := reflect.ValueOf(t)
for i := 0; i < r.NumField(); i++ {
f := r.Field(i)
if f.Kind() == reflect.Ptr && f.Elem().Kind() == reflect.Struct {
validator, ok := f.Interface().(Validator)
if ok {
validator.Validate()
} else {
fmt.Println("not ok")
}
}
}
}
https://play.golang.org/p/hHqP6WdMYqT

Golang polymorphic parameters and returns

Say I have functions:
func ToModelList(cats *[]*Cat) *[]*CatModel {
list := *cats
newModelList := []*CatModel{}
for i := range list {
obj := obj[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
func ToModelList(dogs *[]*Dog) *[]*DogModel {
list := *dogs
newModelList := []*DogModel{}
for i := range list {
obj := obj[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
Is there a way to combine those two so I can do something like
func ToModelList(objs *[]*interface{}) *[]*interface{} {
list := *objs
// figure out what type struct type objs/list are
newModelList := []*interface{}
// type cast newModelList to the correct array struct type
for i := range list {
obj := obj[i]
// type cast obj based on objs's type
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
First, slices are already a reference, unless you need to change the slice itself, you do not need to pass it as a pointer.
Second, an interface{} can be regardless an object or a pointer to an object. You do not need to have *interface{}.
I am not sure what you are trying to achieve but you could do something like this:
package main
// Interface for Cat, Dog
type Object interface {
ToModel() Model
}
// Interface for CatModel, DogModel
type Model interface {
Name() string
}
type Cat struct {
name string
}
func (c *Cat) ToModel() Model {
return &CatModel{
cat: c,
}
}
type CatModel struct {
cat *Cat
}
func (c *CatModel) Name() string {
return c.cat.name
}
type Dog struct {
name string
}
func (d *Dog) ToModel() Model {
return &DogModel{
dog: d,
}
}
type DogModel struct {
dog *Dog
}
func (d *DogModel) Name() string {
return d.dog.name
}
func ToModelList(objs []Object) []Model {
newModelList := []Model{}
for _, obj := range objs {
newModelList = append(newModelList, obj.ToModel())
}
return newModelList
}
func main() {
cats := []Object{
&Cat{name: "felix"},
&Cat{name: "leo"},
&Dog{name: "octave"},
}
modelList := ToModelList(cats)
for _, model := range modelList {
println(model.Name())
}
}
You define interfaces for your Cat, Dogs etc and for your Model. Then you implement them as you want and it is pretty straight forward to do ToModelList().
you can make *CatModel and *DogModel both implement type PetModel {} interface, and just return []Pet in function signature.
func (cats []*Cat) []PetModel {
...
return []*CatModel {...}
}
func (dogs []*Dog) []PetModel {
...
return []*DogModel {...}
}
BTW: return a pointer of a slice in golang is useless.
If you strip away redundant assignments, and unnecessary pointers-to-slices, you'll find you have little code left, and duplicating it for each of your model types doesn't look so bad.
func CatsToCatModels(cats []*Cat) []*CatModel {
var result []*CatModel
for _, cat := range cats {
result = append(result, cat.ToModel())
}
return result
}
Unless this code is used in a lot of places I'd also consider just inlining it, since it's trivial code and only 4 lines when inlined.
Yes, you can replace all the types with interface{} and make the code generic, but I don't think it's a good tradeoff here.

Resources