Is golang []interface{} cannot be function parameter? [duplicate] - algorithm

This question already has answers here:
Type converting slices of interfaces
(9 answers)
Closed 7 years ago.
My code :
package sort_test
type SortList []interface{}
type SortFunc func(interface{}, interface{}) bool
func Do(list SortList, function SortFunc)
main package
package main
import (
"sort_test"
)
func main() {
list := []int{3, 4, 5, 6, 6, 77, 4, 4, 5, 6, 8, 345, 45, 424, 2, 67, 7, 830}
slice := list[:]
sort_test.Do(slice, function)
}
compile error
src/algorithm/algorithm.go:32: cannot use slice (type []int) as type sort_test.SortList in argument to sort_test.Do
src/algorithm/algorithm.go:32: cannot use function (type func(int, int) bool) as type sort_test.SortFunc in argument to sort_test.Do
make: *** [algorithm] Error 2

Cannot. Interface is interface.
interface{} is not some kind of "any" type.
But, any type implements interface{}. Interface is just a set of methods which should be implemented.
If you want to check whether the interface{} is slice or not, you can write it like this:
import "reflect"
t := reflect.TypeOf(list)
if t.Kind() == reflect.Slice {
...
}
I recommend you read this very helpful article: http://blog.golang.org/laws-of-reflection.
Additionally, it will be nice to read the code of sort package: https://golang.org/pkg/sort/. It is an example of golang-way implementation of sorting.
Edit: If you really really want use []interface{} as a parameter, actually you can do it like this:
vs := make([]interface{}, len(list))
for i, e := range list {
vs[i] = e
}
Do(vs, f)
In fact, []interface{} is not an empty interface. It's a slice type whose elements are interface{}; []int is not []interface{}, but just implements interface{}.
I guess you want to write some kind of a general sorting method, like you write it by using generics in Java. I think it is a bad code.

The errors are telling you that you are trying to pass an int array (the slice variable) to the function Do, which is expecting it's first argument as type SortList.
Also, your interface definition does not look right. You have array syntax in there. It should look like this:
type SortList interface{}
I suggest you take a look at the gobyexample page on interfaces.

Related

How do you cast an array of unknown type to type []any? [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
In Go, how do I pass a slice of interface to something that expects slice of a different compatible interface? [duplicate]
(1 answer)
How can I cast from []interface{} to []int? [duplicate]
(1 answer)
Cannot use args (type []string) as type []interface {} [duplicate]
(1 answer)
Convert map[interface {}]interface {} to map[string]string
(3 answers)
Closed 7 months ago.
Assuming only arrays are passed as arguments to the arr parameter, I would like each call of unpackArray() to return the argument casted from its original array type to type []any.
package main
func unpackArray(arr any) []any {
return arr.([]any)
}
func main() {
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
}
However, this code yields error panic: interface conversion: interface {} is []string, not []interface {}. So it is not allowing me to cast an interface whose static type is not type []any to type []any.
So, given I know that arr's static type is some type of array, and without changing the arr parameter's initialization type from any, how could I convert arr to type []any using this function?
(I am encountering the same problem with maps where I cannot cast from an arbitrary map type to type map[any]any, but I am guessing the solution to this issue would be similar to the solution for arrays.)
Go does not have a builtin "cast" like this, but you can write a function to do it.
You may use reflection to convert a slice of any type to []any:
func unpackArray(s any) []any {
v := reflect.ValueOf(s)
r := make([]any, v.Len())
for i := 0; i < v.Len(); i++ {
r[i] = v.Index(i).Interface()
}
return r
}
You can also use generics in Go 1.18 or later:
func unpackArray[S ~[]E, E any](s S) []any {
r := make([]any, len(s))
for i, e := range s {
r[i] = e
}
return r
}
Both versions of these functions work as requested in the question:
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
Notes:
Go does not have "cast" like some other languages. Go has the somewhat related type assertion and conversion features.
The expression arr.([]any) is a type assertion. The expression asserts that the concrete value in the interface arr has type []any. The expression does not do any conversion.
The code in the question uses slices , not arrays as written in the title.
It's not possible to do that directly, because it's not the same thing.
any is the same of interface{} and each interface{} is two-pointers (the first one is the "metadata"/"type-information" and the second one the pointer to the original data).
If you have []uint{60, 42} you have one slice that each element is 8-byte (considering 64bits). So, if you force it to be []any, each element now take 16 bytes, that breaks everything. You can do it using unsafe.
The only way to "cast" is copying the information, so, you can create a new slice of []any and then append each value into that new slice.
One example of copying is:
// You can edit this code!
package main
func unpackArray[T any](arr any) (r []any) {
o := arr.([]T)
r = make([]any, len(o))
for i, v := range o {
r[i] = any(v)
}
return r
}
func main() {
myArr1 := []string{"Hey"}
myArr2 := []int{60}
unpackArray[string](myArr1)
unpackArray[int](myArr2)
}
However, that doesn't make so much sense, since you can use generics in another way.

In Go, can you have function parameters with two distinct types? [duplicate]

This question already has answers here:
Go Generics - Unions
(2 answers)
Closed 11 months ago.
Can you write a function in Go whose parameter can be two different types? For example, if I write a simple function that takes in an array/slice of int and simply returns the first value:
func First(array []int) int {
return array[0]
}
Is there a way to type this such that we can also pass in a []string, etc.? In TypeScript for example, we can do it like such without having to type the array as any:
const first = (array: (number | string)[]): number | string => {
return array[0];
};
I've seen answers explaining the use of interface{} for situations like this... and maybe that's the only way, but it seems to close to any in TS and it feels like there might be a better way of doing this.
(I haven't used Go for a few years, long before they introduced generics - so I'm basing my answer off their documentation for generics)
TypeScript's "generics" (in quotes) aren't really comparable to Go's generics. TypeScript is all about being able to describe an interface (in an abstract sense) for a runtime system built around type-erasure (i.e. JavaScript), while Go's is... honestly, I have no idea. I just don't know how Go's generics are implemented nor their runtime characteristics: Articles on Go's generics, even from the language authors blog or their own documentation site fail to mention key-terms like type erasure, reified generics, (template) instantiation or monomorph, so if anyone has a better understanding please edit this post, or let me know in a comment!
Anyway, the good news is that as-of the Go 1.18 Beta, its support for generics includes support for generic constraints, but also support for union types as a constraint for generic type parameters (though I haven't yet found any information regarding support for other ADTs like product types and intersection types).
(Note that, at least for now, Go won't support union types as concrete types, but in practice that shouldn't be an issue)
In your case, if you want a function that returns the first element of a slice that could be either []int or []string (or returns some default-value if the slice is empty), then you can do this:
func First[T int | string](arr []T, ifEmpty T) T {
for _, v := range arr {
return v
}
return ifEmpty
}
While at first-glance you might think that this would allow for the arr slice to simultaneously contain both int and string values, this is not allowed (see below). Remember that generic parameters arguments are supplied by the caller and have to be valid concrete types, so First can only be instantiated as either First[int] or First[string], which in-turn implies that arr can be either []int or []string.
Anyway, the full example below compiles and runs in the Go dev branch on the Go playground:
package main
import "fmt"
func First[T int | string](arr []T, ifEmpty T) T {
for _, v := range arr {
return v
}
return ifEmpty
}
func main() {
// First[int]:
arrayOfInts := []int{2, 3, 5, 7, 11, 13}
firstInt := First(arrayOfInts, -1)
fmt.Println(firstInt) // 2
// First[string]:
arrayOfStrings := []string{"life", "is short", "and love is always over", "in the morning"}
firstString := First(arrayOfStrings, "empty")
fmt.Println(firstString) // "life"
}
You can also extract the constraint T int | string and move it to an interface, and then use that as the constraint, which I personally think is easier to read, especially when you might need to repeat the same constraint in multiple places:
type IntOrString interface {
int | string
}
func First[T IntOrString](arr []T, ifEmpty T) T {
for _, v := range arr {
return v
}
return ifEmpty
}
Things you can't do...
Note that Go does not (currently, at least) allow using a type that describes a union as a variable's type by itself (nor can you use as a slice's element type either); you can only use a union as a constraint, otherwise you'll get the "interface contains type constraints" error. Which means you can't describe an array that can contain both int and string values and then use that interface for a concrete array type:
package main
import "fmt"
type IntOrString interface {
int | string
}
func First[T IntOrString](arr []T, ifEmpty T) T {
for _, v := range arr {
return v
}
return ifEmpty
}
func main() {
arrayOfIntsOrStrings := []IntOrString{2, "foo", 3, "bar", 5, "baz", 7, 11, 13} // ERROR: interface contains type constraints
firstValue := First(arrayOfIntsOrStrings, -1)
fmt.Println(firstValue)
}
./prog.go:19:28: interface contains type constraints
Go build failed.

Handle "Slice Struct" properly? (golang)

I have created a Slice Struct.
But why can't I append or output values?
package main
import "fmt"
type Slicestruct []struct {
num []int
emptynum []int
}
func main() {
slicestruct := &Slicestruct{
{[]int{1, 2, 3}, []int{}},
{[]int{4, 5, 6}, []int{}},
}
// is working:
fmt.Println(slicestruct)
// isn't working:
fmt.Println(slicestruct[0].num[0])
// isn't working:
slicestruct[0].emptynum = append(slicestruct[0].emptynum, 99)
}
The error message is: "invalid operation: slicestruct[0] (type *Slicestruct does not support indexing)"
You need to dereference the pointer before getting an element
(*slicestruct)[0]
Since it's the actual slice you're accessing an element from, not the pointer.
For pointers to arrays (not slices as you have here), this step would be done automatically.
Here's a related question about pointers to slices and arrays: Pointer to slice and array
Alternatively, you can remove the & when declaring your variable to make it not a pointer type. In the short sample we've seen here, there's nothing to necessitate a pointer. In general, legitimate uses of pointers to slices types are rare.

Remove an interface item from a slice

I want to remove an item in a slice w/o having to use a specific function for every type of items in the slice. So, I am using interface{} as the slice item type:
package main
import "fmt"
func sliceRemoveItem(slice []interface{}, s int) []interface{} {
return append(slice[:s], slice[s+1:]...)
}
func main() {
array := []int{1,2,3,4,5,6,7}
fmt.Println(array)
fmt.Println(sliceRemoveItem(array,1))
}
But goLang doesn't like it:
./prog.go:13:30: cannot use array (type []int) as type []interface {} in argument to sliceRemoveItem
https://play.golang.org/p/wUrR5iGRZ5Y
Any idea how to do this? Is it possible to use a generic single function accepting any type of slice items?
Refs: How to delete an element from a Slice in Golang
You're trying to pass a slice of int as a slice of interface{}. Go doesn't do this conversion implicitly since it is a costly operation.
Check out this answer: https://stackoverflow.com/a/12754757
You can either accept []interface{}, but do the conversion explicitly, or specify the type as []int. This works:
package main
import "fmt"
func sliceRemoveItem(slice []int, s int) []int {
return append(slice[:s], slice[s+1:]...)
}
func main() {
array := []int{1,2,3,4,5,6,7}
fmt.Println(array)
fmt.Println(sliceRemoveItem(array,1))
}
Use the reflect package.
// sliceRemoveItem removes item at index i from the
// slice pointed to by slicep.
func sliceRemoveItem(slicep interface{}, i int) {
v := reflect.ValueOf(slicep).Elem()
v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}
Call it like this:
slice := []int{1, 2, 3, 4, 5, 6, 7}
sliceRemoveItem(&slice, 1)
To avoid type assertions in the caller, the function uses a pointer to slice argument.
Run it on the Go playground

How to implement a generic slice appender? [duplicate]

This question already has answers here:
Appending to go lang slice using reflection
(2 answers)
Closed 8 months ago.
I'm relatively new to go. I'm trying to write a generic "appender" function. This is a simplification, but its an attempt to create a clean interface for processing some lists. Specifically, I have questions about the two errors that this generates:
package main
type GenericFunc func() *interface{}
func Append(ints interface{}, f GenericFunc) {
ints = append(ints, f())
}
func ReturnInt() *int {
i := 1
return &i
}
func main() {
var ints []*int
Append(ints, ReturnInt)
}
Playground
prog.go:5:18: first argument to append must be slice; have interface
{} prog.go:15:11: cannot use ReturnInt (type func() *int) as type
GenericFunc in argument to Append
Why can't ReturnInt be of type GenericFunc? If this doesn't work, I'm not understanding how interface{} can be used with functions at all.. can it?
How can you accept a "generic" slice and append to it using reflection? This would involve checking that GenericFunc returns the same type that the slice is, but after that appending should be possible.
The types func() *interface{} (type type of GenericFunc) and (type func() *int) (the type of ReturnInt) are different types. One returns a *interface{}. The other returns a *int. The types are not assignable to each other.
Use this function to generically append the result of a function to a slice:
func Append(sp interface{}, f interface{}) {
s := reflect.ValueOf(sp).Elem()
s.Set(reflect.Append(s, reflect.ValueOf(f).Call(nil)[0]))
}
Call it like this:
var ints []*int
Append(&ints, ReturnInt)
The function will panic if the argument is not a pointer to a slice or the function does not return a value assignable to a slice element.
playground example

Resources