implement generics in go using interfaces? - go

I am trying to write a stack in Go which works for int and rune array type. But this does not seem to work. I thought that was supposed to work as I have an Interface type to hold any type. The operation of the stack is common for all types. I don't want to use the stack interface that comes with go
package main
import "math"
type expressionStack interface {
pop() interface
getSize() int
push(item interface)
peek() interface
}
type intStack []int
type runeStack []rune
func createStack(st interface) {
return &expressionStack{}
}
func (stack *expressionStack) getSize() int {
return len(stack)
}
func (stack *expressionStack) pop() int {
length := len(stack)
if length == 0 {
panic("Stack is underflown")
}
item := stack[length-1]
stack = stack[:length-1]
return item
}
func (stack *expressionStack) push(item interface) {
stack = append(stack, item)
}
func (stack *expressionStack) peek() int {
leng := len(stack)
if leng == 0 {
return -math.MaxInt32
}
item := stack[leng-1]
return item
}

a solution to those problem is to use code gen.
It requires to arrange a bit the api because generator are pretty dumb, but it does the job.
I start by declaring a package under GOPATH (old school) at test/d/gen
I create a new file template.go, and arranged a bit the code
package gen
type T int
type expressionStack []T
func (stack *expressionStack) getSize() int {
return len(*stack)
}
func (stack *expressionStack) pop() T {
length := len(*stack)
if length == 0 {
panic("Stack is underflown")
}
item := (*stack)[length-1]
*stack = (*stack)[:length-1]
return item
}
func (stack *expressionStack) push(item T) {
*stack = append(*stack, item)
}
func (stack *expressionStack) peek() (zero T, ok bool) {
leng := len(*stack)
if leng == 0 {
return
}
item := (*stack)[leng-1]
return item, true
}
I have tried to use https://github.com/mmatczuk/go_generics witth something like go_generics -t=T=int32 -t=expressionStack=IntStack -i=./template.go -o out.go -p gen, but it failed.
tried another one https://github.com/clementauger/jenjen with jenjen -template=test/d/gen "expressionStack:T => int32, T => -, expressionStack=>Int32Stack" -
I got a neat output
package gen
type Int32Stack []int32
func (stack *Int32Stack) getSize() int {
return len(*stack)
}
func (stack *Int32Stack) pop() int32 {
length := len(*stack)
if length == 0 {
panic("Stack is underflown")
}
item := (*stack)[length-1]
*stack = (*stack)[:length-1]
return item
}
func (stack *Int32Stack) push(item int32) {
*stack = append(*stack, item)
}
func (stack *Int32Stack) peek() (zero int32, ok bool) {
leng := len(*stack)
if leng == 0 {
return
}
item := (*stack)[leng-1]
return item, true
}
the hard stuff was to figure out that "expressionStack:T => int32, T => -, expressionStack=>Int32Stack", its a three statements instructions,
expressionStack: T=>int32 within expressionStack replace T by int32
T => -, within the package, remove T
expressionStack=>Int32Stack, within the package, rename expressionStack to Int32Stack
Though, i had some problem saving the output to the desired file with that last package. not perfect.
there is also a bunch of others package to do that,
https://github.com/cheekybits/genny
https://pkg.go.dev/cmd/gofmt with -r option

I think i found a solution
package main
import (
"math"
"reflect"
)
type expressionStack []interface{}
func (stack *expressionStack) getSize() int {
return len(*stack)
}
func (stack *expressionStack) pop() interface{} {
length := len(*stack)
if length == 0 {
panic("Stack is underflown")
}
item := (*stack)[length-1]
*stack = (*stack)[:length-1]
return item
}
func (stack *expressionStack) push(item interface{}) {
*stack = append(*stack, item)
}
func (stack *expressionStack) peek() interface{} {
leng := len(*stack)
if reflect.TypeOf(stack).Kind() == reflect.Int32 {
if leng == 0 {
return -math.MaxInt32
}
} else {
if leng == 0 {
return 'E'
}
}
item := (*stack)[leng-1]
return item
}

Related

How to transfer interface to specified type by using generic

There is an Interface declare and many structs that implement it
type DataInterface interface {
Get(string) string
}
type DataA struct {
d map[string]string
}
func (d *DataA) Get(key string) string {
return d.d[key]
}
func (d *DataA) GetId() string {
return d.Get("id")
}
type DataB struct {
d map[string]string
}
func (d *DataB) Get(key string) string {
return d.d[key]
}
func (d *DataB) GetFile() string {
return d.Get("file")
}
type DataC...
Also includes DataC,D,E...
and I will store these DataX structs instance into a type DataSlice []DataInterface
Now, If I want to get DataX , I can do this:
type DataSlice []DataInterface
func (d DataSlice) GetA() []*DataA {
var ret []*DataA
for _, di := range d {
if v, ok := di.(*DataA); ok {
ret = append(ret, v)
}
}
return ret
}
func (d DataSlice) GetB() []*DataB {
var ret []*DataB
for _, di := range d {
if v, ok := di.(*DataB); ok {
ret = append(ret, v)
}
}
return ret
}
func (d DataSlice) GetC() .....
Obviously there's a lot of repetitive code here:
var ret []*DataX
for _, di := range d {
if v, ok := di.(*DataX); ok {
ret = append(ret, v)
}
}
So I think about that I can use generic to slove this, then I define this function:
func GetDataX[T any] (d DataInterface) *T {
return d.(*T)
}
but got error: Impossible type assertion: '*T' does not implement 'DataInterface
So, I want to know is this way really impossible? Or it could be completed by the other way?
You should be able to handle your needs with the following code:
package main
import "fmt"
// interface
type DataInterface interface {
Get(string) string
}
// struct implementing the interface
type DataA struct {
d map[string]string
}
func (d DataA) Get(key string) string {
return d.d[key]
}
type DataB struct {
d map[string]string
}
func (d DataB) Get(key string) string {
return d.d[key]
}
type DataSlice []DataInterface
func GetDataX[T any](d DataInterface) T {
return d.(T)
}
func main() {
a := DataA{map[string]string{"a": "1"}}
b := DataB{map[string]string{"b": "2"}}
ds := DataSlice{a, b}
for _, v := range ds {
if value, ok := v.(DataA); ok {
fmt.Printf("A\t%q\n", GetDataX[DataA](value))
continue
}
if value, ok := v.(DataB); ok {
fmt.Printf("B\t%q\n", GetDataX[DataB](value))
continue
}
// add unknown type handling logic here
}
}
First, I simplified the code to take into consideration only the DataA and DataB structs. Then, I changed the pointer receivers to value receivers as you're not going to change the state of the actual instance passed to the methods. Thanks to this change the GetDataX works successfully and you're able to get the info for all of your similar structs.
Let me know if this solves your issues or if you need something else, thanks!

type stack.Stack has no method Push

Writing a stack package.
package stack
type Stack struct {
data []interface{}
}
func (s *Stack) IsEmpty() bool {
return len(s.data) == 0
}
func (s *Stack) Push(item interface{}) {
s.data = append(s.data, item)
//fmt.Println(s.data, item)
}
func (s *Stack) Pop() interface{} {
if len(s.data) == 0 {
return nil
}
index := len(s.data) - 1
res := s.data[index]
s.data = s.data[:index]
return res
}
Then tried to call it
package main
import (
s "library/stack"
t "library/tree"
)
func preorderTraversal(root *t.TreeNode) []int {
res := make([]int, 0)
if root == nil {
return res
}
// iterative
var stack *s.Stack
stack.Push(root)
// TODO: more logic
return res
}
func main() {
left := t.TreeNode{1, nil, nil}
right := t.TreeNode{2, nil, nil}
root := t.TreeNode{0, &left, &right}
fmt.Println(preorderTraversal(&root))
}
But I got an error
stack.Push undefined (type *stack.Stack has no field or method Push)
What is the issue here?
EDIT:
added the tree package just for convenience
package tree
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
added goplayground code based on Cerise Limón play.golang.org/p/E4JXjc-Pd0i
You definitely need to instantiate the stack variable and not just create a nil pointer. Then it should work:
stack := &s.Stack{}
stack.Push(root)
Note:
What confuses me a bit though is the error message. When I run your program it panics: panic: runtime error: invalid memory address or nil pointer dereference

How to check if all fields of a *struct are nil?

I'm not quite sure how to address this question, please feel free to edit.
With the first code block below, I am able to check if a all fields of a struct are nil.
In reality however, the values injected in the struct, are received as args.Review (see second code block below).
In the second code block, how can I check if all fields from args.Review are nil?
Try it on Golang Playground
package main
import (
"fmt"
"reflect"
)
type review struct {
Stars *int32 `json:"stars" bson:"stars,omitempty" `
Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}
func main() {
newReview := &review{
Stars: nil,
// Stars: func(i int32) *int32 { return &i }(5),
Commentary: nil,
// Commentary: func(i string) *string { return &i }("Great"),
}
if reflect.DeepEqual(review{}, *newReview) {
fmt.Println("Nothing")
} else {
fmt.Println("Hello")
}
}
Try the second code on Golang Playground
This code below gets two errors:
prog.go:32:14: type args is not an expression
prog.go:44:27: args.Review is not a type
package main
import (
"fmt"
"reflect"
)
type review struct {
Stars *int32 `json:"stars" bson:"stars,omitempty" `
Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}
type reviewInput struct {
Stars *int32
Commentary *string
}
type args struct {
PostSlug string
Review *reviewInput
}
func main() {
f := &args {
PostSlug: "second-post",
Review: &reviewInput{
Stars: func(i int32) *int32 { return &i }(5),
Commentary: func(i string) *string { return &i }("Great"),
},
}
createReview(args)
}
func createReview(args *struct {
PostSlug string
Review *reviewInput
}) {
g := &review{
Stars: args.Review.Stars,
Commentary: args.Review.Commentary,
}
if reflect.DeepEqual(args.Review{}, nil) {
fmt.Println("Nothing")
} else {
fmt.Println("Something")
}
}
If you're dealing with a small number of fields you should use simple if statements to determine whether they are nil or not.
if args.Stars == nil && args.Commentary == nil {
// ...
}
If you're dealing with more fields than you would like to manually spell out in if statements you could use a simple helper function that takes a variadic number of interface{} arguments. Just keep in mind that there is this: Check for nil and nil interface in Go
func AllNil(vv ...interface{}) bool {
for _, v := range vv {
if v == nil {
continue
}
if rv := reflect.ValueOf(v); !rv.IsNil() {
return false
}
}
return true
}
if AllNil(args.Stars, args.Commentary, args.Foo, args.Bar, args.Baz) {
// ...
}
Or you can use the reflect package to do your bidding.
func NilFields(x interface{}) bool {
rv := reflect.ValueOf(args)
rv = rv.Elem()
for i := 0; i < rv.NumField(); i++ {
if f := rv.Field(i); f.IsValid() && !f.IsNil() {
return false
}
}
return true
}
if NilFields(args) {
// ...
}

How can I compare struct data and interface data in Golang?

I am trying to create a generic Binary Tree in Golang. How can I compare data from an interface and input data in the code? Here is an example of what I am trying to do. The comparison that is giving me trouble is this
} else if cur.data < data {
-
package DSAA
type TreeNode struct {
data interface{}
right *TreeNode
left *TreeNode
}
type BinarySearchTree struct {
root *TreeNode
}
func BSTCreate() *BinarySearchTree {
return &BinarySearchTree{nil}
}
func (b *BinarySearchTree) Insert(cur TreeNode, data interface{}) *BinarySearchTree {
if &cur == nil {
cur := &TreeNode{data, nil, nil}
} else if cur.data < data {
b = b.Insert(*cur.left, data)
} else {
b = b.Insert(*cur.right, data)
}
return b
}
You have some options:
1- Using runtime type switch:
package main
import (
"fmt"
)
func main() {
fmt.Println(Less(1, 2)) // true
fmt.Println(Less("AB", "AC")) // true
}
func Less(a, b interface{}) bool {
switch v := a.(type) {
case int:
w := b.(int)
return v < w
case string:
w := b.(string)
return v < w
}
return false
}
then replace } else if cur.data < data { with
} else if Less(cur.data , data) {
2- Using Comparer interface:
package main
import (
"fmt"
)
type Comparer interface {
// Less reports whether the element is less than b
Less(b interface{}) bool
}
func main() {
a, b := Int(1), Int(2)
fmt.Println(a.Less(b)) // true
c, d := St("A"), St("B")
fmt.Println(c.Less(d)) // true
}
type Int int
func (t Int) Less(b interface{}) bool {
if v, ok := b.(Int); ok {
return int(t) < int(v)
}
return false
}
type St string
func (t St) Less(b interface{}) bool {
if v, ok := b.(St); ok {
return string(t) < string(v)
}
return false
}
3- Using reflect

Golang container/list creating a FindAll function

I was wondering if this is the way to create and pass 'generic'(yeah I know, a sensitive word in GoLang) lists to a FindAll function.
Here's my attempt:
package main
import (
"container/list"
"fmt"
"strings"
)
func FindAll(lst *list.List, p func(interface{}) bool) *list.List {
ans := list.New()
for i := lst.Front(); i != nil; i = i.Next() {
if p(i.Value) {
ans.PushBack(i.Value)
}
}
return ans
}
func ConvertToInt(p func(int) bool) func(interface{}) bool {
return func(v interface{}) bool {
if value, ok := v.(int); ok {
if p(value) {
return true
} else {
return false
}
} else {
return false
}
}
}
func IsEven(n int) bool {
if n%2 == 0 {
return true
}
return false
}
func ConvertoString(p func(s string) bool) func(interface{}) bool {
return func(v interface{}) bool {
if value, ok := v.(string); ok {
if p(value) {
return true
} else {
return false
}
} else {
return false
}
}
}
func IsHello(str string) bool {
if strings.ToLower(str) == "hello" {
return true
} else {
return false
}
}
func main() {
fmt.Println("Find All Programs!\n\n")
lsti := list.New()
for i := 0; i < 11; i++ {
lsti.PushBack(i)
}
ansIsEven := FindAll(lsti, ConvertToInt(IsEven))
for i := ansIsEven.Front(); i != nil; i = i.Next() {
if value, ok := i.Value.(int); ok {
fmt.Printf("Found even: %d\n", value)
} else {
fmt.Println("Huh! What's that?")
}
}
}
I've been playing with this for a while and thought I'd better get the advice of the Go experts before I convince myself its correct.
The code as-is is pretty fine, but you should ask your self 2 questions:
1. Why shouldn't you use a typed slice? (interface{} performance is slow compared to the explicit type, although it will greatly improve in Go 1.7)
2. Would it be better to implement your specific type as a linked list?
Something like this can be much more efficient:
type IntList []int
func (l IntList) Filter(fn func(v int) bool) IntList {
var o IntList
for _, v := range l {
if fn(v) {
o = append(o, v)
}
}
return o
}
There's almost always a better alternative to container/list, however it all depends on your use case.

Resources