variadic function in golang - go

package main
import (
"fmt"
)
type ISum interface {
sum() int
}
type SumImpl struct {
Num int
}
func (s SumImpl) sum() int {
return s.Num
}
func main() {
nums := []int{1, 2}
variadicExample1(nums...)
impl1 := SumImpl{Num: 1}
impl2 := SumImpl{Num: 2}
variadicExample2(impl1, impl2)
impls := []SumImpl{
{
Num: 1,
},
{
Num: 2,
},
}
variadicExample2(impls...)
}
func variadicExample1(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func variadicExample2(nums ...ISum) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num.sum()
}
fmt.Println(total)
}
I have a question while using variable functions in go language.
When passing a struct that implements an interface as an argument, individual declarations are possible, but can you tell me why it is not possible when passing it through ...?
An error occurs in the code below.
variadicExample2(impls...)
I read this
How to pass an interface argument to a variadic function in Golang?
var impls []ISum
impls = append(impls, impl1)
impls = append(impls, impl1)
variadicExample2(impls...)
I found that the above code is possible.

A SumImpl slice is not a ISum slice. One is a slice of structs, and the other is a slice of interfaces. That's why you cannot pass it to a function that requires a []ISum (i.e. ...ISUm).
But you can do this:
impls := []ISum{
SumImpl{
Num: 1,
},
SumImpl{
Num: 2,
},
}

Related

Is Golang's ... syntax really just varargs?

Well, I have a simple example, although it may seem pointless, but these examples have deviated greatly from my cognition.
Can anyone tell me what happened.
I pass the elements in []int to the parameter args with the ... syntax, but when I change the formal parameter args, the []int actual parameter is changed.
I modified the ordering of args , but the ordering of []int is also affected:
package main
import (
"fmt"
)
func bubbleSort(args ...int) {
for i := 0; i < len(args); i++ {
for j := 0; j < len(args)-1; j++ {
if args[j] > args[j+1] {
args[j], args[j+1] = args[j+1], args[j]
}
}
}
}
func main() {
isle := []int{3, 6, 1, 2, 5}
bubbleSort(isle...)
fmt.Printf("%v\n", isle)
}
// [1 2 3 5 6]
What if I pass in a single parameter? Apparently []int is not affected by args:
package main
import (
"fmt"
)
func bubbleSort(args ...int) {
for i := 0; i < len(args); i++ {
for j := 0; j < len(args)-1; j++ {
if args[j] > args[j+1] {
args[j], args[j+1] = args[j+1], args[j]
}
}
}
}
func main() {
isle := []int{3, 6, 1, 2, 5}
bubbleSort(isle[0], isle[1], isle[2], isle[3], isle[4])
fmt.Printf("%v\n", isle)
}
// [3 6 1 2 5]
Golang is not about "variable safety" as Rust. So you shoud never think that slice sent to function will not be corrupted.
If you want to use variadic functions but send slices, copy it:
func myUnsafeFunc(args ...int) {
// it will change args
}
func wantToSaveSliceFunc() {
myLovingSlice := []int{1,2,3}
myUnsafeFunc(append([]int(nil), slice...)...)
}
https://freshman.tech/snippets/go/copy-slices/
It's not confusing if you remember that spread operator ... is just a shugar .
Do not use it in your own API without serious reasone (as in fmt.Printf(...) for example) prefer use of explicit x []type notation over x ...type:
func myUnsafeFunc(args []int) {
// it will change args
}
func wantToSaveSliceFunc() {
myLovingSlice := []int{1,2,3}
myUnsafeFunc(append([]int(nil), slice...))
}
it still requires copying of slice, but it's much clear by semantics

How to assign an interface variable to the value stored by another

Description of the problem:
args[0] = ... updates args[0]:
package main
import "fmt"
func MyFunc(lambda any) {
myVars := []any {0}
for i := 0; i < 30; i++ {
lambda.(func(...any))(myVars...)
fmt.Println(myVars[0]) // 0, 2, 4, ..., 60 (good)
}
}
func main() {
MyFunc(func(args ...any) {
args[0] = args[0].(int) + 2
})
}
But when I make variable v := args[0] and attempt to update the value of args[0] by doing v = ..., Go (understandably) reassigns v to a new object rather than updating the value of args[0]:
package main
import "fmt"
func MyFunc(lambda any) {
myVars := []any {0}
for i := 0; i < 30; i++ {
lambda.(func(...any))(myVars...)
fmt.Println(myVars[0]) // 0, 0, 0, ..., 0 (bad)
}
}
func main() {
MyFunc(func(args ...any) {
v := args[0]
v = v.(int) + 2
})
}
My question:
How, using v, can I update args[0]? Any help will be greatly appreciated.
Things I have tried:
I cannot do *v = ..., as this yields compiler error "invalid operation: cannot indirect v (variable of type any)".
I cannot do v := args[0].(*int); *v = *v + 2;, as this yields runtime error "panic: interface conversion: interface {} is int, not *int".
You did not do the pointer operations correctly. Type-assert the pointer variable v. First take the address of the arg value with &, then proceed with rest of your logic.
func main() {
MyFunc(func(args ...any) {
v := &args[0]
*v = (*v).(int) + 2
})
}
Go playground

Using functional options instead of method

I tried the below, and it worked well:
package main
import "fmt"
type T interface {
}
type hashMap struct {
m map[T]T
k []T
}
func (h *hashMap) From(m map[T]T) {
h.m = m
h.k = make([]T, len(m))
i := 0
for key := range m {
h.k[i] = key
i++
}
}
func main() {
inv := new(hashMap)
inv.From(map[T]T{"first": 1})
fmt.Printf("%v", inv)
}
The output was correct as expected:
&{map[first:1] [first]}
I'm looking for a way where I can write:
inv := new(hashMap).from(map[T]T{"first": 1})
// instead of:
// inv := new(hashMap)
// inv.From(map[T]T{"first": 1})
So, I re-wrote the code as below:
package main
import "fmt"
type T interface {
}
type hashMap struct {
m map[T]T
k []T
from func(m map[T]T) hashMap // <---- new
}
func from(m map[T]T) hashMap { // <----- change of func signature
h := new(hashMap)
h.m = m
h.k = make([]T, len(m))
i := 0
for key := range m {
h.k[i] = key
i++
}
return *h
}
func main() {
inv := new(hashMap).from(map[T]T{"first": 1})
fmt.Printf("%v", inv)
}
But I got the below output:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x49c2c2]
goroutine 1 [running]:
main.main()
d:/goplay/hashmap.go:55 +0xf2
exit status 2
Any idea how to get it done the way I'm looking for?
You are very close to getting it! First, since you already create the hashMap in main() you don't need to create again in from(). Just delete line 14 and change the signature of from() like this:
func (h *hashMap) from(m map[T]T) hashMap {
Also delete line 11 - in Go you don't need to declare a method of a type just define it.
Try it in the playground
Also it's idiomatic to build a slice using append:
func (h *hashMap) from(m map[T]T) hashMap {
h.m = m
h.k = make([]T, 0, len(m))
for key := range m {
h.k = append(h.k, key)
}
return *h
}

How to determine the element type of slice interface{}?

I have the following code to double the slice.
func doubleSlice(s []int) []int {
t := make([]int, len(s), (cap(s) + 1) * 2 )
for i := range s {
t[i] = s[i]
}
return t
}
I want to make the func to double any type of slice. And I need to know the element type first.
func showInterfaceItem(s interface{}) interface{} {
if reflect.TypeOf(s).Kind() != reflect.Slice {
fmt.Println("The interface is not a slice.")
return
}
var t interface{}
newLen := reflect.ValueOf(s).Len()
newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
t = make([]reflect.TypeOf(s), newLen, newCap)
return t
}
The reflect.TypeOf(s) return the type of interface{}, not the type of element. How can I get the element type of slice interface?
You can use reflect.TypeOf(s).Elem()
to get the type of element of slice.
package main
import (
"fmt"
"reflect"
)
func doubleSlice(s interface{}) interface{} {
if reflect.TypeOf(s).Kind() != reflect.Slice {
fmt.Println("The interface is not a slice.")
return nil
}
v := reflect.ValueOf(s)
newLen := v.Len()
newCap := (v.Cap() + 1) * 2
typ := reflect.TypeOf(s).Elem()
t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
reflect.Copy(t, v)
return t.Interface()
}
func main() {
xs := doubleSlice([]string{"foo", "bar"}).([]string)
fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
ys := doubleSlice([]int{3, 1, 4}).([]int)
fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
}
The output will be:
data = [foo bar] len = 2 cap = 6
data = [3 1 4] len = 3 cap = 8
Check it in: Go Playground
This is doable in golang and takes me whole day to discover the pattern.
Firstly, we want to get a pointer of slice to make gorm happy, which is has type "*[]Obj". To achieve that in golang, we can create a make wrapper like so:
func makeWrapper(cap uint) interface{} {
arr:= make([]Sth, 0, cap)
return &arr
}
Notice that, we can't directly reference the maked value, which might be the book keeping data need to have a stack space to store.
//Not working example
func makeWrapper(cap uint) interface{} {
return &(make([]Sth, 0, cap))
}
And as the answer before, the reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() returns interface{[]Sth}. (the typ here is refer to reflect.TypeOf(Sth{}), which equiv to typ == reflect.TypeOf(v))
Thus we need to create a return object of *[]Sth and the value inside is a slice []Sth with capacity. After understanding the objective, we can have this code:
package main
import (
"reflect"
)
type Sth struct {
a, b string
}
func main() {
af:= createSlice(Sth{})
arr := makeWrapper(10).(*[]Sth)
println(reflect.TypeOf(arr).String())
// equiv to makeWrapper, but we do it via reflection
arr = af(10).(*[]Sth)
println(reflect.TypeOf(arr).String())
}
func makeWrapper(cap uint) interface{} {
arr:= make([]Sth, 0, cap)
return &arr
}
func createSlice(v interface{}) func(int) interface{} {
var typ reflect.Type
if reflect.ValueOf(v).Kind() == reflect.Ptr {
typ = reflect.ValueOf(v).Elem().Type()
} else if reflect.ValueOf(v).Kind() == reflect.Struct {
typ = reflect.TypeOf(v)
} else {
panic("only support instance of struct or pointer of that instance")
}
return func(capacity int) interface{}{
// create the outer object saves our slice
outerObj:=reflect.New(reflect.SliceOf(typ))
// create the slice and save it to return
outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
// retrive the interface of outer object
return outerObj.Interface()
}
}

Short way to apply a function to all elements in a list in golang

Suppose I would like to apply a function to every element in a list, and then put the resulting values in another list so I can immediately use them. In python, I would do something like this:
list = [1,2,3]
str = ', '.join(multiply(x, 2) for x in list)
In Go, I do something like this:
list := []int{1,2,3}
list2 := []int
for _,x := range list {
list2 := append(list2, multiply(x, 2))
}
str := strings.Join(list2, ", ")
Is it possible to do this in a shorter way?
I would do exactly as you did, with a few tweaks to fix typos
import (
"fmt"
"strconv"
"strings"
)
func main() {
list := []int{1,2,3}
var list2 []string
for _, x := range list {
list2 = append(list2, strconv.Itoa(x * 2)) // note the = instead of :=
}
str := strings.Join(list2, ", ")
fmt.Println(str)
}
This is an old question, but was the top hit in my Google search, and I found information that I believe will be helpful to the OP and anyone else who arrives here, looking for the same thing.
There is a shorter way, although you have to write the map function yourself.
In go, func is a type, which allows you to write a function that accepts as input the subject slice and a function, and which iterates over that slice, applying that function.
See the Map function near the bottom of this Go by Example page : https://gobyexample.com/collection-functions
I've included it here for reference:
func Map(vs []string, f func(string) string) []string {
vsm := make([]string, len(vs))
for i, v := range vs {
vsm[i] = f(v)
}
return vsm
}
You then call it like so:
fmt.Println(Map(strs, strings.ToUpper))
So, yes: The shorter way you are looking for exists, although it is not built into the language itself.
I've created a small utility package with Mapand Filter methods now that generics have been introduced in 1.18 :)
https://pkg.go.dev/github.com/sa-/slicefunk
Example usage
package main
import (
"fmt"
sf "github.com/sa-/slicefunk"
)
func main() {
original := []int{1, 2, 3, 4, 5}
newArray := sf.Map(original, func(item int) int { return item + 1 })
newArray = sf.Map(newArray, func(item int) int { return item * 3 })
newArray = sf.Filter(newArray, func(item int) bool { return item%2 == 0 })
fmt.Println(newArray)
}
With go1.18+ you can write a much cleaner generic Map function:
func Map[T, V any](ts []T, fn func(T) V) []V {
result := make([]V, len(ts))
for i, t := range ts {
result[i] = fn(t)
}
return result
}
Usage, e.g:
input := []int{4, 5, 3}
outputInts := Map(input, func(item int) int { return item + 1 })
outputStrings := Map(input, func(item int) string { return fmt.Sprintf("Item:%d", item) })
Found a way to define a generic map array function
func Map(t interface{}, f func(interface{}) interface{} ) []interface{} {
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)
arr := make([]interface{}, s.Len())
for i := 0; i < s.Len(); i++ {
arr[i] = f(s.Index(i).Interface())
}
return arr
}
return nil
}
origin := []int{4,5,3}
newArray := Map(origin, func(item interface{}) interface{} { return item.(int) + 1})
You can use lo's Map in order to quickly apply a function to all elements. For example, in order to multiply by 2 and convert to string, you can use:
l := lo.Map[int, string]([]int{1, 2, 3, 4}, func(x int, _ int) string { return strconv.Itoa(x * 2) })
Then you can convert back to a comma delimited string like so:
strings.Join(l, ",")

Resources