creating generic functions for multi type arrays in Go - go

I am trying to create a generic function that can handle actions on slices in Go... for instance, append an item of any type to a slice of that same type. This is simply a generic purpose for a more complex solution, but overall the issue boils down to this example:
package main
type car struct {
make string
color string
}
type submarine struct {
name string
length int
}
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
func main() {
cars := make([]car, 0, 10)
cars[0] = car{make: "ford", color: "red"}
cars[1] = car{make: "chevy", color: "blue"}
subs := make([]submarine, 0, 10)
subs[0] = submarine{name: "sally", length: 100}
subs[1] = submarine{name: "matilda", length: 200}
newCar := car{make: "bmw", color: "white"}
genericAppender(&cars, newCar)
}
The code playground is at this location
The above errors as follows:
prog.go:14: first argument to append must be slice; have interface {}

After this change you're still getting a runtime error (index out of range) however the problem is that thingList is not of type []interface{} but rather interface{} so you can't append to it. Here's an updated version of your code on playground that does a type assertion to convert it to an []interface{} in line with the append. In reality you need to do that on a separate line and check for errors.
https://play.golang.org/p/YMed0VDZrv
So to put some code here;
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList.([]interface{}), thing)
}
will solve the basic problem you're facing. As noted, you still get runtime errors when indexing into the slice. Also, you could change the argument to avoid this by making it;
func genericAppender(thingList []interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
Here's a complete example of the second type; https://play.golang.org/p/dIuW_UG7XY
Note I also corrected the runtime error. When you use make with 3 args they are, in this order, type, length, capacity. This means the length of the array is 0 so when you try to assign to indexes 0 and 1 it was causing a panic for IndexOutoFRange. Instead I removed the middle argument so it's make([]interface{}, 10) meaning the length is initially set to 10 so you can assign to those indexes.

In the answer above if you do the following then it throws error. This is what the original question was about:
//genericAppender(subs, newCar). // Throws "cannot use subs (type []submarine) as type []interface {} in argument to genericAppender"
The trick is to convert your slice of specific type into a generic []interface{}.
func convertToGeneric(thingList interface{}) []interface{} {
input := reflect.ValueOf(thingList)
length := input.Len()
out := make([]interface{},length)
for i:=0 ;i < length; i++ {
out[i] = input.Index(i).Interface()
}
return out
}
This you can call the function like this:
genericAppender(convertToGeneric(subs), newCar)
You can check modified working code here: https://play.golang.org/p/0_Zmme3c8lT

With Go 1.19 (Q4 2022), no need for interface, or "convert your slice of specific type into a generic []interface{}"
CL 363434 comes with a new slices packages:
// Package slices defines various functions useful with slices of any type.
// Unless otherwise specified, these functions all apply to the elements
// of a slice at index 0 <= i < len(s).
package slices
import "constraints"
// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, Grow panics.
func Grow[S ~[]T, T any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[T comparable](s1, s2 []T) bool {
if len(s1) != len(s2) {
return false
}
for i, v1 := range s1 {
v2 := s2[i]
if v1 != v2 {
return false
}
}
return true
}
// ...
Ian Lance Taylor confirms in issue 45955:
This package is now available at golang.org/x/exp/slices.
Per this thread, it will not be put into standard library until the 1.19 release.
We may of course adjust it based on anything we learn about having it in x/exp.

Related

How to cast multiple variables' interfaces to dynamic types together

I know that for a single variable x, to check if it is of a certain type B, just do
switch b.(type) {
case *B:
fmt.Println("find it!")
default:
fmt.Println("can't find it")
}
But now I have a slice of 4 variables, and I'd like to know if their types follow a certain pattern (e.g. of type A,B,C,D).
I know I can do it with a tedious forloop, with many ifs and cases wrapping together, but I wonder if there's a more elegant way to achieve what I want.
You could use reflect against some "truth" slice that you define. This function will take in 2 slices and compare their types, returning an error if the types do not match in the same order.
So arr is your []interface{} slice.
exp is the expected slice, such as
// The values don't matter, only the type for the "truth" slice.
exp := []interface{}{int(0), "", Foo{}, Bar{}}
See https://goplay.tools/snippet/5nja8M00DSt
// SameTypes will compare 2 slices. If the slices have a different length,
// or any element is a different type in the same index, the function will return
// an error.
func SameTypes(arr, exps []interface{}) error {
if len(arr) != len(exps) {
return errors.New("slices must be the same length")
}
for i := range arr {
exp := reflect.TypeOf(exps[i])
found := reflect.TypeOf(arr[i])
if found != exp {
return fmt.Errorf("index '%d' expected type %s, got %s", i, exp, found)
}
}
return nil
}
Keep in mind Foo{} and &Foo{} are different types. If you don't care if it's a pointer, you will have to do additional reflect code. You can do this to get the value of the ptr if the type is a pointer.
x := &Foo{}
t := reflect.TypeOf(x)
// If t is a pointer, we deference that pointer
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// t is now of type Foo

Why [capacity]string assert to []string will be failed in Golang?

I am using Golang1.14.
Here is the test code.
package main
import "time"
func checkData(data interface{}) {
if _, ok := data.([]string); ok {
println("Assert true.")
} else {
println("Assert false.")
}
}
func main() {
var years [20]string
for i := 0; i < 20; i++ {
years[i] = string(time.Now().Year() - 10 + i)
}
checkData(years)
foods := []string{"Fruit", "Grass", "Fish", "Meat"}
checkData(foods)
}
The output is:
Assert false.
Assert true.
I am new to Golang and really confusing that [20]string is not a []string.Can someone tell me why?Thanks.
[20]string is an array. It is a type that contains 20 strings, and if you pass it as an interface{}, you can recover it using intf.([20]string).
[]string is a slice. It has a backing array, but it is essentially a view over an array. You assertion checks if the interface is a slice, so this one works.
Arrays and slices are different things in Go. An array is a data type with a fixed size. For instance:
func f(arr [10]int) {...}
You can only call f with an int array of size 10. When you do call it, the array will be passes as value, so the function will get a copy of the array, all 10 members of it. But:
func f(arr []int) {...}
You can call f with any size of slice. A slice contains a reference to its underlying array, so an array copy will not take place here. You cannot call thisf` with an array.

Are slices passed by value?

In Go, I am trying to make a scramble slice function for my traveling salesman problem. While doing this I noticed when I started editing the slice I gave the scramble function was different every time I passed it in.
After some debugging I found out it was due to me editing the slice inside the function. But since Go is supposed to be a "pass by value" language, how is this possible?
https://play.golang.org/p/mMivoH0TuV
I have provided a playground link to show what I mean.
By removing line 27 you get a different output than leaving it in, this should not make a difference since the function is supposed to make its own copy of the slice when passed in as an argument.
Can someone explain the phenomenon?
Everything in Go is passed by value, slices too. But a slice value is a header, describing a contiguous section of a backing array, and a slice value only contains a pointer to the array where the elements are actually stored. The slice value does not include its elements (unlike arrays).
So when you pass a slice to a function, a copy will be made from this header, including the pointer, which will point to the same backing array. Modifying the elements of the slice implies modifying the elements of the backing array, and so all slices which share the same backing array will "observe" the change.
To see what's in a slice header, check out the reflect.SliceHeader type:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
See related / possible duplicate question:
Performance of function slice parameter vs global variable?
Read blog post: Go Slices: usage and internals
Please note that when you pass a slice to a function, if the function modifies the "existing" elements of the slice, the caller will see / observe the changes. If the function adds new elements to the slice, that requires changing the slice header (the length at a minimum, but may also involve allocating a new backing array), which the caller will not see (not without returning the new slice header).
Not with maps, because maps are pointers under the hood, and if you pass a map to a function and the function adds a new entry to the map, the map pointer will not change so the caller will see the changed map (the new entry) without returning the map after change.
Also regarding slices and maps, see Map initialization in Go and why slice values can sometimes go stale but never map values?
You can find an example below. Briefly slices is also passed by value but original slice and copied slice are linked to the same underlying array. If one of this slice changes, then underlying array changes, then other slice changes.
package main
import "fmt"
func main() {
x := []int{1, 10, 100, 1000}
double(x)
fmt.Println(x) // ----> 3 will print [2, 20, 200, 2000] (original slice changed)
}
func double(y []int) {
fmt.Println(y) // ----> 1 will print [1, 10, 100, 1000]
for i := 0; i < len(y); i++ {
y[i] *= 2
}
fmt.Println(y) // ----> 2 will print [2, 20, 200, 2000] (copy slice + under array changed)
}
Slices when its passed it’s passed with the pointer to underlying array, so a slice is a small structure that points to an underlying array. The small structure is copied, but it still points to the same underlying array. the memory block containing the slice elements is passed by "reference". The slice information triplet holding the capacity, the number of element and the pointer to the elements is passed by value.
The best way to handle slices passing to function (if the elements of the slice are manipulated into the function, and we do not want this to be reflected at the elements memory block is to copy them using copy(s, *c) as:
package main
import "fmt"
type Team []Person
type Person struct {
Name string
Age int
}
func main() {
team := Team{
Person{"Hasan", 34}, Person{"Karam", 32},
}
fmt.Printf("original before clonning: %v\n", team)
team_cloned := team.Clone()
fmt.Printf("original after clonning: %v\n", team)
fmt.Printf("clones slice: %v\n", team_cloned)
}
func (c *Team) Clone() Team {
var s = make(Team, len(*c))
copy(s, *c)
for index, _ := range s {
s[index].Name = "change name"
}
return s
}
But be careful, if this slice is containing a sub slice further copying is required, as we'll still have the sub slice elements sharing pointing to the same memory block elements, an example is:
type Inventories []Inventory
type Inventory struct { //instead of: map[string]map[string]Pairs
Warehouse string
Item string
Batches Lots
}
type Lots []Lot
type Lot struct {
Date time.Time
Key string
Value float64
}
func main() {
ins := Inventory{
Warehouse: "DMM",
Item: "Gloves",
Batches: Lots{
Lot{mustTime(time.Parse(custom, "1/7/2020")), "Jan", 50},
Lot{mustTime(time.Parse(custom, "2/1/2020")), "Feb", 70},
},
}
inv2 := CloneFrom(c Inventories)
}
func (i *Inventories) CloneFrom(c Inventories) {
inv := new(Inventories)
for _, v := range c {
batches := Lots{}
for _, b := range v.Batches {
batches = append(batches, Lot{
Date: b.Date,
Key: b.Key,
Value: b.Value,
})
}
*inv = append(*inv, Inventory{
Warehouse: v.Warehouse,
Item: v.Item,
Batches: batches,
})
}
(*i).ReplaceBy(inv)
}
func (i *Inventories) ReplaceBy(x *Inventories) {
*i = *x
}
Slice will work with pass by value to the function, But we should not use append to add values to slice in the function, instead we should use the assignment directly. Reason being that append will create new memory and copy values to that. Here is the example.
Go playground
// Go program to illustrate how to
// pass a slice to the function
package main
import "fmt"
// Function in which slice
// is passed by value
func myfun(element []string) {
// Here we only modify the slice
// Using append function
// Here, this function only modifies
// the copy of the slice present in
// the function not the original slice
element = append(element, "blackhole")
fmt.Println("Modified slice: ", element)
}
func main() {
// Creating a slice
slc := []string{"rocket", "galaxy", "stars", "milkyway"}
fmt.Println("Initial slice: ", slc)
//slice pass by value
myfun(slc)
fmt.Println("Final slice: ", slc)
}
Output-
Initial slice: [rocket galaxy stars milkyway]
Modified slice: [rocket galaxy stars milkyway blackhole]
Final slice: [rocket galaxy stars milkyway]
Go Playground
// Go program to illustrate how to
// pass a slice to the function
package main
import "fmt"
// Function in which slice
// is passed by value
func myfun(element []string) {
// Here we only modify the slice
// Using append function
// Here, this function only modifies
// the copy of the slice present in
// the function not the original slice
element[0] = "Spaceship"
element[4] = "blackhole"
element[5] = "cosmos"
fmt.Println("Modified slice: ", element)
}
func main() {
// Creating a slice
slc := []string{"rocket", "galaxy", "stars", "milkyway", "", ""}
fmt.Println("Initial slice: ", slc)
//slice pass by value
myfun(slc)
fmt.Println("Final slice: ", slc)
}
Output-
Initial slice: [rocket galaxy stars milkyway ]
Modified slice: [Spaceship galaxy stars milkyway blackhole cosmos]
Final slice: [Spaceship galaxy stars milkyway blackhole cosmos]
To complement this post, here is an example of passing by reference for the Golang PlayGround you shared:
type point struct {
x int
y int
}
func main() {
data := []point{{1, 2}, {3, 4}, {5, 6}, {7, 8}}
makeRandomDatas(&data)
}
func makeRandomDatas(dataPoints *[]point) {
for i := 0; i < 10; i++ {
if len(*dataPoints) > 0 {
fmt.Println(makeRandomData(dataPoints))
} else {
fmt.Println("no more elements")
}
}
}
func makeRandomData(cities *[]point) []point {
solution := []point{(*cities)[0]} //create a new slice with the first item from the old slice
*cities = append((*cities)[:0], (*cities)[1:]...) //remove the first item from the old slice
return solution
}

Strange behaviour when passing a struct property (slice) to a function that removes elements from it

I've started learning Go these days and got stuck in trying to pass a struct property's value (a slice) to a function. Apparently it's being passed as a reference (or it holds a pointer to its slice) and changes made inside the function affect it.
Here is my code, in which testFunction is supposed to receive a slice, remove its first 3 elements and print the updated values, but without affecting it externally:
package main
import (
"fmt"
)
type testStruct struct {
testArray []float64
}
var test = testStruct {
testArray: []float64{10,20,30,40,50},
}
func main() {
fmt.Println(test.testArray)
testFunction(test.testArray)
fmt.Println(test.testArray)
}
func testFunction(array []float64) {
for i:=0; i<3; i++ {
array = removeFrom(array, 0)
}
fmt.Println(array)
}
func removeFrom(array []float64, index int) []float64 {
return append(array[:index], array[index+1:]...)
}
That outputs:
[10 20 30 40 50]
[40 50]
[40 50 50 50 50]
My question is: what is causing the third fmt.Println to print this strange result?
Playground: https://play.golang.org/p/G8W3H085In
p.s.: This code is only an example. It's not my goal to remove the first elements of something. I just wanna know what is causing this strange behaviour.
Usually we don't know whether a given call to append will cause a reallocation, so we can't assume that the original slice refers to the same array as the resulting slice, nor that it refers to a different one.
To use slices correctly, it's important to remember that although the elements of the underlying array are indirect, the slice's pointer, length and capacity are not.
As a result, it's usual to assign the result of a call to append to the same slice variable:
array = append(array, ...)
So to sum up, to receive the desired result always remember to assign the append function to a new or the same slice variable.
Here is the corrected and working code:
package main
import (
"fmt"
)
type testStruct struct {
testArray []float64
}
var test = testStruct {
testArray: []float64{10,20,30,40,50},
}
func main() {
fmt.Println(test.testArray)
a := testFunction(test.testArray)
fmt.Println(a)
}
func testFunction(array []float64)[]float64 {
for i:=0; i<3; i++ {
array = removeFrom(array, 0)
}
fmt.Println(array)
return array
}
func removeFrom(array []float64, index int) []float64 {
return append(array[:index], array[index+1:]...)
}
Check it the working code on Go Playground.
Another solution is to pass the array argument via pointer reference:
func testFunction(array *[]float64) {
for i:=0; i<3; i++ {
*array = removeFrom(*array, 0)
}
fmt.Println(*array)
}
Go Playground
The slice is a composite type. It has a pointer to the data, the length and the capacity. When you pass it as an argument you're passing those values, the pointer, the length and the capacity; they are copies, always.
In your case you modify the data within the slice when you call removeFrom(), which you can do because you've copied the value of a pointer to the original data into the func, but the length and capacity remain unchanged outside the scope of that function as those are not pointers.
So, when you print it again from main() you see the altered values but it still uses the original length and capacity as any changes made to those within the scope of the other funcs were actually on copies of those values.
Here is a useful blog post about slices https://blog.golang.org/slices. It states this in particular.
It's important to understand that even though a slice contains a
pointer, it is itself a value. Under the covers, it is a struct value
holding a pointer and a length. It is not a pointer to a struct.
The reason you see [40 50 50 50 50] is because you changed the values in the slice, but you did not alter the slice itself(it's cap and len)

How to delete an element from any slice without generics? [duplicate]

This question already has answers here:
How to delete an element from a Slice in Golang
(20 answers)
Closed 10 months ago.
I have a function removeFrom that removes an item from a slice. It accepts a float64 slice and an index:
func removeFrom(slice []float64, index int) []float64 {
if len(slice) > index {
return append(slice[:index], slice[index+1:]...)
}
}
It works fine, but now I have to remove from slices of integers also. So how could I change this to accept both types (and return a slice of the given type)? I tried to use empty interfaces but apparently I have to do some conversion inside the function and I didn't find out how to do it.
Short answer? you can't.
Long answer, you still can't directly do it, BUT:
func removeFrom(slice interface{}, index int) interface{} {
switch slice := slice.(type) {
case []float64:
if len(slice) > index {
return append(slice[:index], slice[index+1:]...)
}
case []int64:
if len(slice) > index {
return append(slice[:index], slice[index+1:]...)
}
case []int:
if len(slice) > index {
return append(slice[:index], slice[index+1:]...)
}
default:
log.Panicf("unknown type: %T", slice)
}
}
Go doesn't support generics, there is no "common ancestor" for all slice types ([]interface{} is not "compatible" with []int for example, see Cannot convert []string to []interface {} for more details).
So if you want your function to accept any slice types, you have to use interface{} (both for the "incoming" parameter and for the return type). But now you have an (interface) wrapper value to which you can't apply slicing and which you can't pass to the builtin append() function.
You could use type assertion and type switches for known types, but you would have to repeat code for each, so it's not really a step ahead.
Actually there's a way to create a removeFrom() function that will work for all slice types, with using reflection.
reflect.Value is a type describing any Go value. It has supporting methods for different types of Go values, including slices.
What's interesting to us is the Value.Slice() method:
func (v Value) Slice(i, j int) Value
We can use it to slice a slice. Good. It is a key point in our element-removal algorithm. What's still needed is to "join" 2 slices, the one before and the one after the removable element. Luckily the reflect package also has support for this: reflect.AppendSlice():
func AppendSlice(s, t Value) Value
As the last remaining key, we can use Value.Len() to get the length of any slice.
We now have everything that's needed for our general removeFrom() function, which is surprisingly simple:
func removeFrom(s interface{}, idx int) interface{} {
if v := reflect.ValueOf(s); v.Len() > idx {
return reflect.AppendSlice(v.Slice(0, idx), v.Slice(idx+1, v.Len())).Interface()
}
return s
}
Really, that's all. Testing it:
for i := 0; i < 4; i++ {
fmt.Println(removeFrom([]int{0, 1, 2}, i), "missing:", i)
}
for i := 0; i < 4; i++ {
fmt.Println(removeFrom([]string{"zero", "one", "two"}, i), "missing:", i)
}
Output (try it on the Go Playground):
[1 2] missing: 0
[0 2] missing: 1
[0 1] missing: 2
[0 1 2] missing: 3
[one two] missing: 0
[zero two] missing: 1
[zero one] missing: 2
[zero one two] missing: 3
Notes:
This solution uses reflection, so it will be slower than another solution not using reflection but having the concrete, supported types "wired in". Quick benchmarks show this general solution is 2.5 times slower than a non-reflection with wired-in types. Should be weighted whether performance or convenience/general solution is more important. Or you may combine this with concrete types: you may add a type switch to handle frequent types, and only revert to this general solution if the actual concrete type is not handled by the type switch.

Resources