Is it possible to store a Go type - go

I've got a handful of interfaces, and n number of structs that arbitrarily implement these interfaces. I'd like to keep an array of types and be able to run a loop over them to see which ones are implemented. Is it possible to store a type like this? I spent a little bit of time with the reflect package, but couldn't really find what I was looking for, I understand if maybe this isn't best practice. Trying to do something similar to this.. without a giant type switch, fallthrough, or if.. if... if.
type InterOne interface {
InterOneMethod() string
}
var interfaceMap = map[string]type {
"One": InterOne,
...
}
func doesHandle(any interface{}) []string {
var handles []string
for k, v := range interfaceMap {
if _, ok := any.(v); ok {
handles = append(handles, k)
}
}
return handles
}
EDIT: The answer marked as correct is technically right. I found that due to the comment about the method calling & the overuse of reflection, that this approach was a bad idea. Instead I went with a type switch to check for a single interface because fallthrough is not supported on type switch, and a large if.. if.. if.. with type assertions to be able to make the appropriate calls.

You can use reflect, notice that to get the type of an interface the only way is to use reflect.TypeOf((*INTERFACE)(nil)).Elem(), here's a working example:
var interfaceMap = map[string]reflect.Type{
"One": reflect.TypeOf((*InterOne)(nil)).Elem(),
....
}
func doesHandle(any interface{}) []string {
t := reflect.TypeOf(any)
var handles []string
for k, v := range interfaceMap {
if t.Implements(v) {
handles = append(handles, k)
}
}
return handles
}
playground

Related

Idiomatic and DRY solution to merging arrays of arbitrary types

I want to create a utility-function that is able to merge two given slices, determining equality by a given function.
type IsEqualTest func(interface{}, interface{}) bool
func ArrayMerge(one *[]interface{}, another *[]interface{}, comp IsEqualTest) *[]interface{} {
merged := *one
for _, element := range *another {
if !ArrayContains(one, &element, comp) {
merged = append(merged, element)
}
}
return &merged
}
func ArrayContains(container *[]interface{}, eventualContent *interface{}, comp IsEqualTest) bool {
for _, element := range *container {
if comp(element, eventualContent) {
return true
}
}
return false
}
// please don't mind the algorithmic flaws
However, as go does treat the []interface{} type as non-compatible to slices of anything (and it lacks generics), I would need to iterate over both operands, converting the type of the contained elements when calling, which is not what anyone could want.
What is the Go style of dealing with collections containing any type?
First: without generics, there is no idiomatic way of doing this.
Second: your thinking might be too influenced by other languages. You already got a function to compare, why not take it a bit further?
What I suggest below is not efficient, and it should not be done. However, if you really want to do it:
It looks like this is not a set union, but add the elements of the second slice to the first if they don't already exist in the first slice. To do that, you can pass two functions:
func merge(len1,len2 int, eq func(int,int)bool, write func(int)) {
for i2:=0;i2<len2;i2++ {
found:=false
for i1:=0;i1<len1;i1++ {
if eq(i1,i2) {
found=true
break
}
}
if !found {
write(i2)
}
}
Above, eq(i,j) returns true if slice1[i]==slice2[j], and write(j) does append(result,slice2[j]).

Function that takes map and only cares about key type

I have two maps, both of them are keyed by strings, but the values are of two different custom types.
map[string]type1
map[string]type2
Now I want to write a function which can take an argument of either of these two types, because that function only looks at the keys and doesn't care about the values at all. So I think it should look like this:
func takeTheMap(argument map[string]interface{}) {
...
But that doesn't work due to:
cannot use myVariable (type map[string]customType) as type map[string]interface {} in argument to takeTheMap
https://play.golang.org/p/4Xkhi4HekO5
Can I make that work somehow?
The only polymorphism in Go is interfaces. The only alternatives to that are reflection, duplication, or rethinking the broader design so that you don't need to do what you're trying to do here.
If the last option isn't a possibility, personally I would recommend duplication, since it's a whole four lines of code.
keys := make([]string, 0, len(myMap))
for key,_ := range myMap {
keys = append(keys,key)
}
A big complicated generic helper seems kind of unnecessary.
A solution using an interface. This example may seem a bit overkill and it may be better to in your case (I'm not sure, not enough details in your example) to just use a couple of for loops.
package main
import (
"fmt"
)
type foo bool
type bar string
type mapOne map[string]foo
type mapTwo map[string]bar
func (m mapOne) Keys() []string {
s := []string{}
for k := range m {
s = append(s, k)
}
return s
}
func (m mapTwo) Keys() []string {
s := []string{}
for k := range m {
s = append(s, k)
}
return s
}
type ToKeys interface {
Keys() []string
}
func main() {
m1 := mapOne{"one": true, "two": false}
m2 := mapTwo{"three": "foo", "four": "bar"}
doSomething(m1)
doSomething(m2)
}
func doSomething(m ToKeys) {
fmt.Println(m.Keys())
}
Playground example

Cannot Range Over List Type Interface {} In Function Using Go

Cannot Range Over List Type Interface {} In Function Using Go.
for me is important then i execute for in a function.
How can fix?
package main
import (
"fmt"
)
type MyBoxItem struct {
Name string
}
type MyBox struct {
Items []MyBoxItem
}
func (box *MyBox) AddItem(item MyBoxItem) []MyBoxItem {
box.Items = append(box.Items, item)
return box.Items
}
func PrintCustomArray(list interface{}) interface{} {
//items := reflect.ValueOf(list)
for _, v := range list {
fmt.Println(v.Key,v.Value)
}
return 0
}
func main() {
items := []MyBoxItem{}
item := MyBoxItem{Name: "Test Item 1"}
box := MyBox{items}
box.AddItem(item)
fmt.Println((box.Items))
PrintCustomArray(box.Items)
}
https://play.golang.org/p/ZcIBLMliq3
Error : cannot range over list (type interface {})
How can fix?
Note
The answer below describes, in broad strokes, 2 possible approaches: using interfaces, and using specific types. The approach focusing on interfaces is mentioned for completeness sake. IMHO, the case you've presented is not a viable use-case for interfaces.
Below, you'll find a link to a playground example that uses both techniques. It should be apparent to anyone that the interface approach is too cumbersome if for this specific case.
Quite apart from the fact that you don't really seem to be too familiar with how loops work in go (v.Key and v.Value are non-existent fields for example), I'll attempt to answer your question.
You are passing a list to your function, sure enough, but it's being handled as an interface{} type. That means your function accepts, essentially, any value as an argument. You can't simply iterate over them.
What you can do is use type assertions to convert the argument to a slice, then another assertion to use it as another, specific interface:
type Item interface{
key() string
val() string
}
func (i MyBoxItem) key() string {
return i.Key
}
func (i MyBoxItem) val() string {
return i.Value
}
func PrintCustomArray(list interface{}) error {
listSlice, ok := list.([]interface{})
if !ok {
return fmt.Errorf("Argument is not a slice")
}
for _, v := range listSlice {
item, ok := v.(Item)
if !ok {
return fmt.Errorf("element in slice does not implement the Item interface")
}
fmt.Println(item.key(), item.val())
}
return nil
}
But let's be honest, a function like this only works if a slice is passed as an argument. So having that first type assertion in there makes no sense whatsoever. At the very least, changing the function to something like this makes a lot more sense:
func PrintCustomArray(list []interface{})
Then, because we're not expecting an array as such, but rather a slice, the name should be changed to PrintCustomSlice.
Lastly, because we're using the same type assertion for every value in the slice, we might as well change the function even more:
// at this point, we'll always return 0, which is pointless
// just don't return anything
func PrintCustomSlice(list []Item) {
for _, v := range list {
fmt.Println(v.key(), v.val())
}
}
The advantages of a function like this is that it can still handle multiple types (all you have to do is implement the interface). You don't need any kind of expensive operations (like reflection), or type assertions.
Type assertions are very useful, but in a case like this, they merely serve to hide problems that would otherwise have resulted in a compile-time error. Go's interface{} type is a very useful thing, but you seem to be using it to get around the type system. If that's what you want to achieve, why use a typed language in the first place?
Some closing thoughts/remarks: If your function is only going to be used to iterate over specific "thing", you don't need the interfaces at all, simply specify the type you're expecting to be passed to the function in the first place. In this case that would be:
func PrintCustomSlice(list []MyBoxItem) {
for _, v := range list {
fmt.Println(v.Key, v.Value)
}
}
Another thing that I've noticed is that you seem to be exporting everything (all functions, types, and fields start with a capital letter). This, in go, is considered bad form. Only export what needs to be public. In the main package, that usually means you're hardly export anything.
Lastly, as I mentioned at the start: you don't seem to have a firm grasp on the basics just yet. I'd strongly recommend you go through the interactive tour. It covers the basics nicely, but shows you the features of the language at a decent pace. It doesn't take long, and is well worth taking a couple of hours to complete
Playground demo
It's possible to implement PrintCustomArray using the reflect package, but most experienced Go programmers will write a simple for loop:
for _, i := range box.Items {
fmt.Println("Name:", i.Name)
}
https://play.golang.org/p/RhubiCpry0
You can also encapsulate it in a function:
func PrintCustomArray(items []MyBoxItem) {
for _, i := range items {
fmt.Println("Name:", i.Name)
}
}
https://play.golang.org/p/c4EPQIx1AH
Here since you are returning box.Items from AddItem(), Items is of the type []MyBoxItem , so list should be of type []MyBoxItem .Moreover you are returning 0 in PrintCustomArray and the return type you have set is {}interface.
func PrintCustomArray(list []MyBoxItem) {
//items := reflect.ValueOf(list)
for i, v := range list {
fmt.Println(i, v)
}
//return 0
}
Again, MyBoxItem struct has only one variable named Name so v.key v.value won't make any sense.
This is what the proper code should look like https://play.golang.org/p/ILoUwEWv6Y .
You need to clear your understanding about interfaces in go. This might help https://golang.org/doc/effective_go.html#interfaces_and_types .

How to resolve whether pass objects via interface{} have not initializated fields

I have problem with resolve whether object which was pass as interface to function hasn't initializated fields, like object which was defined as just someObject{} is a empty, because all fields, has value 0, or nil
Problem becomes more complicated if I pass diffrent objects, because each object have diffrent type field value so on this moment I don't find universal way to this.
Example
func main(){
oo := objectOne{}
ot := objectTwo{}
oth := objectThree{"blah" , "balbal" , "blaal"}
resolveIsNotIntialized(oo)
resolveIsNotIntialized(ot)
resolveIsNotIntialized(oth)
}
func resolveIsNotIntialized(v interface{}) bool{
// and below, how resolve that oo and ot is empty
if (v.SomeMethodWhichCanResolveThatAllFiledIsNotIntialized){
return true
}
return false
}
I want to avoid usage switch statement like below, and additional function for each object, ofcorse if is possible.
func unsmartMethod(v interface{}) bool{
switch v.(type){
case objectOne:
if v == (objectOne{}) {
return true
}
// and next object, and next....
}
return false
}
As Franck notes, this is likely a bad idea. Every value is always initialized in Go. Your actual question is whether the type equals its Zero value. Generally the Zero value should be designed such that it is valid. The better approach would generally be to create an interface along the lines of:
type ZeroChecker interface {
IsZero() bool
}
And then attach that to whatever types you want to check. (Or possibly better: create an IsValid() test instead rather than doing your logic backwards.)
That said, it is possible to check this with reflection, by comparing it to its Zero.
func resolveIsNotIntialized(v interface{}) bool {
t := reflect.TypeOf(v)
z := reflect.Zero(t).Interface()
return reflect.DeepEqual(v, z)
}
(You might be able to get away with return v == z here; I haven't thought through all the possible cases.)
I don’t think there is a good reason (in idiomatic Go) to do what you are trying to do. You need to design your structs so that default values (nil, empty string, 0, false, etc.) are valid and represent the initial state of your object. Look at the source of the standard library, there are lots of examples of that.
What you are suggesting is easily doable via Reflection but it will be slow and clunky.
You could narrow the type which your function takes as an argement a little, not take an interface{} but accept one that allows you to check for non-zero values, say type intercae{nonZero() bool} as in the example code below. This will not tell you explicitly that it hasn't been set to the zero value, but that it is not zero.
type nonZeroed interface {
nonZero() bool
}
type zero struct {
hasVals bool
}
func (z zero) nonZero() bool {
return z.hasVals
}
type nonZero struct {
val int
}
func (nz nonZero) nonZero() bool {
return nz.val != 0
}
type alsoZero float64
func (az alsoZero) nonZero() bool {
return az != 0.0
}
func main() {
z := zero{}
nz := nonZero{
val: 1,
}
var az alsoZero
fmt.Println("z has values:", initialized(z))
fmt.Println("nz has values:", initialized(nz))
fmt.Println("az has values:", initialized(az))
}
func initialized(a nonZeroed) bool {
return a.nonZero()
}
Obviously as the type get more complex additional verification would need to be made that it was "nonZero". This type of pattern could be used to check any sort condition.

Code Duplication in Go Type Switches

Just getting started writing Go code and I ran into an interesting problem.
Is there a way to easily iterate over the items in an array that is brought in as an empty interface without code duplication? Consider the following:
function(someArr interface{}){
switch someArr.(type){
case []int :
arr := (someArr).([]int)
for i := range (arr) {
// CODE
}
case []string :
arr := (someArr).([]string)
for i := range (arr) {
// CODE
}
}
}
In this example the code in CODE is exactly the same. However, I cannot take it out of the switch because the type assertion arr would fall out of scope. Similarly, I can't define arr before the switch because I don't know what type it will be. It's possible that this just cannot be done. In that case, what's a better idiom for this kind of thing when I'm, say, parsing JSON with an irregular schema (some arrays of ints, some arrays or strings)?
Your example is not idiomatic Go code, even though the idiomatic one lexically seems violating the DRY principle as well.
The key point to understand is that 'x' is a separate, differently typed variable in each type case:
function(someArr interface{}){
switch x := someArr.(type) {
case []int:
for i := range x {
// CODE
}
case []string:
for i := range x {
// CODE
}
}
}
You can use the reflect package to iterate over arbitrary slices. But implementing the special cases (like []int) explicitly is generally faster and is often done in addition to avoid reflection in common cases.
package main
import "fmt"
import "reflect"
func foo(values interface{}) {
rv := reflect.ValueOf(values)
if rv.Kind() != reflect.Slice {
return
}
n := rv.Len()
for i := 0; i < n; i++ {
value := rv.Index(i).Interface()
fmt.Println(value)
}
}
func main() {
foo([]int{1, 3, 3, 7})
}
Edit: I'm not sure why somebody has down voted the question and my answer, but there are cases where you need to deal with code like that. Even the standard library contains plenty of it, take a look at "fmt", "gob", "json", "xml" and "template" for example. The questioner might face a similar problem.

Resources