golang multiple case in type switch - go

when I run the code snippet bellow, it raise a error
a.test undefined (type interface {} is interface with no methods)
It seem the type switch does not take effect.
package main
import (
"fmt"
)
type A struct {
a int
}
func(this *A) test(){
fmt.Println(this)
}
type B struct {
A
}
func main() {
var foo interface{}
foo = A{}
switch a := foo.(type){
case B, A:
a.test()
}
}
If I change it to
switch a := foo.(type){
case A:
a.test()
}
it's ok now.

This is normal behaviour that is defined by the spec (emphasis mine):
The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the beginning of the implicit block in each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.
So, in fact, the type switch does take effect, but the variable a keeps the type interface{}.
One way you could get around this is to assert that foo has the method test(), which would look something like this:
package main
import (
"fmt"
)
type A struct {
a int
}
func (this *A) test() {
fmt.Println(this)
}
type B struct {
A
}
type tester interface {
test()
}
func main() {
var foo interface{}
foo = &B{}
if a, ok := foo.(tester); ok {
fmt.Println("foo has test() method")
a.test()
}
}

Related

Go - storing structs with the same embedded struct in a list

I have multiple structs with the same embedded struct. In one of the structs I would like to store a list of any struct that embeds the base struct. Here is a small snippet showing the case.
package main
type Symbol interface{}
type Base struct {
a int32
}
type Foo struct {
Base
b int32
Symbols []Base
// Below works
// Symbols []Symbol
}
type Bar struct {
Base
c int32
}
func main () {
var bar Bar
var foo Foo
foo.Symbols = append(foo.Symbols, bar)
}
However, this does not work. I get ./main.go:25:22: cannot use bar (type Bar) as type Base in append. When I use the empty Symbol interface, everything works. However this approach completely bypasses the type system as now everything can be stored in the list. I would like to somehow denote that only Base, Foo and Bar can be stored in the list, so that compiler can check if this rquirement is met. My structs do not have any methods, for sure not the ones that share behavior and can be added to the interface. Adding some dummy method to the interface and dummy implementations seems very artificial. What is the Go idiomatic way of handling such scenarios?
What matters is the same interface:
package main
import (
"fmt"
)
type Item interface{
fmt.Stringer
}
type ItemA struct {
}
func (a ItemA) String() string {
return "item A"
}
type ItemB struct {
}
func (a ItemB) String() string {
return "item B"
}
func main() {
items := make([]Item, 0)
items = append(items, ItemA{}, ItemB{})
fmt.Printf("Items: %v", items)
}
What you seem to be expecting is subtype polymorphism. Go does not support dynamic binding for non-interface types. Therefore you you could either use interfaces
package main
func main(){
var bar Bar
var foo Foo
foo.Symbols = append(foo.Symbols, bar)
}
type Symbol interface {
Symbol()
}
type Base struct {
a int32
}
func (b Base)Symbol(){}
type Foo struct {
Base
b int32
Symbols []Symbol
}
type Bar struct {
Base
c int32
}
or if you dislike using interfaces, you can use a trick like below with reflect.
package main
import "fmt"
func main(){
var bar Bar
var foo Foo
err := foo.AddSymbol(bar)
if err != nil{
fmt.Println(err)
}
}
type Base struct {
a int32
}
func (b Base)Symbol(){}
type Foo struct {
Base
b int32
symbol []interface{} // field made private
}
// AddSymbol : helper to append values
func (f *Foo)AddSymbol(in interface{})(err error){
if f.symbol == nil{
f.symbol = make([]interface{},0)
}
switch typ := in.(type) {
case Base,Bar,Foo:
f.symbol = append(f.symbol,in)
default:
return fmt.Errorf("provided type: %s is not supported",typ)
}
return nil
}
type Bar struct {
Base
c int32
}
I did some searching and reading. What I want to achieve requires so called "sum types". Currently Go does not support sum types. However, there are few alternative to simulate the behavior of sum types. They are quite nicely described here Alternatives to sum types in Go.
What is more, it looks like sum types will probably be supported in the Go 2. Further reading proposal: spec: add sum types / discriminated unions, spec: generics: use type sets to remove type keyword in constraints.

Dynamic Type Assertion Golang

I'm trying to convert an interface dynamically back to it's original struct but I am having issues accessing attributes of the struct after the conversion.
Take this code for example.
package main
import (
"fmt"
"log"
)
type struct1 struct {
A string
B string
}
type struct2 struct {
A string
C string
}
type struct3 struct {
A string
D string
}
func main() {
s1 := struct1{}
s1.A = "A"
structTest(s1)
s2 := struct2{}
s2.A = "A"
structTest(s2)
s3 := struct3{}
s3.A = "A"
structTest(s3)
}
func structTest(val interface{}) {
var typedVal interface{}
switch v := val.(type) {
case struct1:
fmt.Println("val is struct1")
case struct2:
fmt.Println("val is struct2")
case struct3:
fmt.Println("val is struct3")
default:
log.Panic("not sure what val is.")
}
fmt.Println(typedVal.A)
}
I would like to be able to pass in one of 3 known struct types into my function. Then figure out which struct type was passed in to type assert it. Finally I want to be able to access like attributes.
Basically I want to have some basic inheritance in my structs, but so far it seems that it is not possible to do this in go. I saw some posts mentioning inheritance using an interface, but my structs have no methods so I'm not sure how I would use an interface.
Is something like this possible in go?
I would like to be able to pass in one of 3 known struct types into my function. Then figure out which struct type was passed in to type assert it. Finally I want to be able to access like attributes.
You can use type assertions to do exactly that. Basic idea is, in any case of the type switch just use type assertion to get a concrete instance of the corresponding type and then you can call whatever properties that you wish.
Take a look at the following example
package main
import (
"fmt"
)
type test1 struct {
A, B string
}
type test2 struct {
A, C string
}
func testType(val interface{}) {
switch val.(type) {
case test1:
t := val.(test1)
fmt.Println(t.B)
break
case test2:
t := val.(test2)
fmt.Println(t.C)
break
}
}
func main() {
t1, t2 := test1{B: "hello"}, test2{C: "world"}
testType(t1)
testType(t2)
}
Playground
Function structTest(val interface{}) in your code seems to be loosely typed. You pass it an untyped argument and expect it will satisfy some condition (will have field A), it looks strange in any typed language.
Using an interface this kind of polymorphism, in Go, to my mind, can be expressed something like
package main
import (
"fmt"
"log"
)
type A string
type HasA interface {
PrintA()
}
func (a A) PrintA() { fmt.Println(a) }
type struct1 struct {
A
B string
}
type struct2 struct {
A
C string
}
type struct3 struct {
A
D string
}
func main() {
s1 := struct1{}
s1.A = "A"
structTest(s1)
s2 := struct2{}
s2.A = "A"
structTest(s2)
s3 := struct3{}
s3.A = "A"
structTest(s3)
}
func structTest(val HasA) {
switch val.(type) {
case struct1:
fmt.Println("val is struct1")
case struct2:
fmt.Println("val is struct2")
case struct3:
fmt.Println("val is struct3")
default:
log.Panic("not sure what val is.")
}
val.PrintA()
}
Playground

How can I prevent initialization of an exported type in Go?

Edit: I'm not asking how to initialize variables correctly. I'm asking how to prevent them from being initialized incorrectly, so that functions that take that type don't have to explicitly validate their arguments.
In Go, I have a type that contains maps, e.g.:
package foo
type Bar struct {
baz map[string]int
total int
}
func NewBar() *Bar {
return &Bar{baz: map[string]int{}}
}
func (b *Bar) IncrementBaz(key string) {
f.baz[key] = f.baz[key] + 1
f.total++
}
// ...
If another package initializes the type and then tries to write to it, it will try to write to a nil map, causing a segfault:
package main
import "foo"
func main() {
var b foo.Bar
b.IncrementBaz("some_key") // write to nil map -> segfault
// ...
}
To prevent this, I want to make it so that other packages can't initialize an empty foo.Bar.
I can't simply make it unexported, because I want functions in other packages to be able to declare it as an argument or return type. If I wrap it in an interface, I get a similar problem:
package "foo"
type Bar interface {
IncrementBaz(string)
// ...
}
type bar struct {
baz map[string]int
total int
}
func NewBar() Bar {
return &bar{baz: map[string]int{}}
}
func (b *bar) IncrementBaz(key string) {
f.baz[key] = f.baz[key] + 1
f.total++
}
// ...
Similar problem:
package main
import "foo"
func main() {
var b foo.Bar
b.IncrementBaz("some_key") // method call on nil interface -> segfault
// ...
}
Is there any way to have an exported type that only the package that declares it can initialize it?
Now foo.Bar is just an interface type. The default value is a nil pointer. You are very close, just initialize it as b := foo.NewBar()

Why does method signature have to perfectly match interface method

I began to learn Go and have trouble understanding the following:
package main
import "fmt"
type I interface {
foo(interface {})
}
type S struct {}
func (s S) foo(i int) {
fmt.Println(i)
}
func main() {
var i I = S{}
i.foo(2)
}
This fails with:
cannot use S literal (type S) as type I in assignment:
S does not implement I (wrong type for foo method)
have foo(int)
want foo(interface {})
I don't understand why Go doesn't accept the foo(int) signature given the fact that int implements interface {}. Can anyone help with an explanation?
I think your understanding of interface isn't sound. Interface{} itself is a type. It consists of two things : underlying type and underlying value.
Golang doesn't have overloading. Golang type system matches by name and requires consistency in the types
So, when you are defining a function taking a interface type as a parameter:
foo(interface {})
This is a different function from a function taking int type:
foo(int)
So you should change the following line to
func (s S) foo(i interface{}) {
fmt.Println(i)
}
Or better yet to this:
type I interface {
foo()
}
type S struct {
I int
}
func (s S) foo() {
fmt.Println(s.I)
}
func main() {
var i I = S{2}
i.foo()
}
The error message itself is self-explaining:
"S does not implement I (wrong type for foo method)"
so S{} which is of S type cannot be used on the RHS of type I variable assignment.
To implement I interface, type S needs to define foo function with the exact same signature.
To achieve what you wanted, you can use empty interface as input parameter for your foo function in S and do type assertion
package main
import "fmt"
type I interface {
foo(interface {})
}
type S struct {}
func (s S) foo(i interface{}) {
if i, ok := i.(int); ok{
fmt.Println(i)
}
}
func main() {
var i I = S{}
i.foo(2)
i.foo("2")
}
Try it in go playground

Object Factory in golang

I am a new to golang. I need to design a function to create object of differing types based on input. But I failed to figure out how to design the interface. Here comes my code:
package main
import (
"fmt"
)
type AA struct{
name string
}
func (this *AA) say(){
fmt.Println("==========>AA")
}
type BB struct{
*AA
age int
}
func (this *BB) say(){
fmt.Println("==========>BB")
}
func ObjectFactory(type int) *AA {
if type ==1 {
return new(AA)
}else{
return new(BB)
}
}
func main() {
obj1 := ObjectFactory(0)
obj1.say()
obj2 := ObjectFactory(0)
obj2.say()
}
The compiler tells me error no matter I ask ObjectFactory return *AA or interface{}. How can I make it work?
First off, using type as a variable name is disallowed in go (see the spec). That is your first problem.
The return type of object factory is *AA. This means that it can only return variables of type *AA, which causes the return of type of BB to fail. As defined in the spec, go doesn't have type inheritance, just struct embedding.
If you create an interface called sayer, you can use that instead of *AA in your ObjectFactory function.
type sayer interface {
say()
}
You probably want to use this interface when trying to get multiple dispatch (as demonstrated in the code below (see on play.golang.org as well).
Try this code:
package main
import (
"fmt"
)
type sayer interface {
say()
}
type AA struct{
name string
}
func (this *AA) say(){
fmt.Println("==========>AA")
}
type BB struct{
*AA
age int
}
func (this *BB) say(){
fmt.Println("==========>BB")
}
func ObjectFactory(typeNum int) sayer {
if typeNum ==1 {
return new(AA)
}else{
return new(BB)
}
}
func main() {
obj1 := ObjectFactory(1)
obj1.say()
obj2 := ObjectFactory(0)
obj2.say()
}

Resources