How to use multiple expressions in single case statement? - go

In the below code:
package main
import (
"fmt"
"reflect"
)
type Model1 struct {
ID string
}
type Model2 struct {
ID string
}
func main() {
ch1 := make(chan Model1)
close(ch1)
checkIfChannelClosed(ch1)
ch2 := make(chan Model2)
close(ch2)
checkIfChannelClosed(ch2)
}
func checkIfChannelClosed(ch interface{}) bool {
if reflect.TypeOf(ch).Kind() != reflect.Chan {
fmt.Println("only channels can be closed")
return false
}
ok := true
if ch == nil {
return false
}
switch v := ch.(type) {
case chan Model1:
select {
case _, ok = <-v: // Line 26
default:
}
case chan Model2:
select {
case _, ok = <-v:
default:
}
default:
fmt.Println("Invalid case")
}
if ok {
fmt.Println("channel is open")
} else {
fmt.Println("channel is closed")
}
return ok
}
GoLang compiler does not allow to write multiple expressions in case statement(as shown below). Goal is to avoid redundant code for select:
switch v := ch.(type) {
case chan Model1, chan Model2:
select {
case _, ok = <-v:
default:
}
default:
fmt.Println("Invalid case")
}
How to use multiple expressions with case statement?

I read this in "The Go programming language" chapter 7.13:
In this style, the emphasis is on the concrete types that satisfy the interface, not on the interface’s methods (if indeed it has any),and there is no hiding of information.
So, i think x.(Type) return a concrete type,if you use a multicase in a swith x:=x.(Type), what happend in the follow code?
switch v := ch.(type) {
case chan Model1,int:
//do something
}
just use the reflect.value to do this:
func checkIfChannelClosed(ch interface{}) bool {
v := reflect.ValueOf(ch)
if v.Kind() != reflect.Chan {
fmt.Println("only channels can be closed")
return false
}
_, ok := v.TryRecv()
if ok{
fmt.Println("recv value from channel..")
}else{
fmt.Println("channel is closed or receive cannot finish without blocking")
}
return ok
}

Related

why I push false to channel once, but select got false twice in golang?

I am a newbie in golang, I am studying concurrency in golang, and tried to wrote a simple crawler demo, when I read all given url, I push a false to processChannel, and this push just will execute once;
then in other goroutine, I select on processChannel, when got a false, I closed channel for application, but, in this select case, I got false twice, and got a panic for "panic: close of closed channel"
so, I cannot understand why I pushed false once, but select case false twice ???
All code at below:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
var applicationStatus bool
var urls []string
var urlsProcessed int
var foundUrls []string
var fullText string
var totalURLCount int
var wg sync.WaitGroup
var v1 int
func main() {
applicationStatus = true
statusChannel := make(chan int)
textChannel := make(chan string)
processChannel := make(chan bool)
totalURLCount = 0
urls = append(urls, "https://www.msn.cn/zh-cn/news/other/nasa%E7%AC%AC%E4%BA%94%E6%AC%A1%E8%A7%82%E5%AF%9F%E5%88%B0%E9%BB%91%E6%B4%9E%E5%90%83%E6%8E%89%E4%B8%80%E9%A2%97%E6%B5%81%E6%B5%AA%E7%9A%84%E6%81%92%E6%98%9F/ar-AA15ybhx?cvid=0eaf927e48604c0588413d393c788a8f&ocid=winp2fptaskbarent")
urls = append(urls, "https://www.msn.cn/zh-cn/news/other/nasa%E7%AC%AC%E4%BA%94%E6%AC%A1%E8%A7%82%E5%AF%9F%E5%88%B0%E9%BB%91%E6%B4%9E%E5%90%83%E6%8E%89%E4%B8%80%E9%A2%97%E6%B5%81%E6%B5%AA%E7%9A%84%E6%81%92%E6%98%9F/ar-AA15ybhx?cvid=0eaf927e48604c0588413d393c788a8f&ocid=winp2fptaskbarent")
fmt.Println("Starting spider")
urlsProcessed = 0
totalURLCount = len(urls)
go evaluateStatus(statusChannel, processChannel)
go readURLs(statusChannel, textChannel)
go appendToFullText(textChannel, processChannel)
for {
if applicationStatus == false {
fmt.Println(fullText)
fmt.Println("Done!")
break
}
//select {
//case sC := <-statusChannel:
// fmt.Println("Message on statusChannel", sC)
//}
}
}
func evaluateStatus(statusChannel chan int, processChannel chan bool) {
for {
select {
case status := <-statusChannel:
urlsProcessed++
if status == 0 {
fmt.Println("got url")
}
if status == 1 {
close(statusChannel)
}
if urlsProcessed == totalURLCount {
fmt.Println("=============>>>>urlsProcessed")
fmt.Println(urlsProcessed)
fmt.Println("read all top-level url")
processChannel <- false
applicationStatus = false
}
}
}
}
func readURLs(statusChannel chan int, textChannel chan string) {
time.Sleep(time.Millisecond * 1)
fmt.Println("grabing ", len(urls), " urls")
for _, url := range urls {
resp, _ := http.Get(url)
text, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("No HTML body")
}
textChannel <- string(text)
statusChannel <- 0
}
}
func appendToFullText(textChannel chan string, processChannel chan bool) {
for {
select {
case pC := <-processChannel:
fmt.Println("pc==============>>>")
fmt.Println(pC)
if pC == true {
// hang out
}
if pC == false {
// all url got
close(textChannel)
close(processChannel)
}
case tC := <-textChannel:
fmt.Println("text len: ")
fmt.Println(len(tC))
fullText += tC
}
}
}
Thx for your help.
As per the Go Programming Language Specification
A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.
This can be seen in the following (playground) demonstration (the comments show what is output):
func main() {
processChannel := make(chan bool)
go func() {
processChannel <- true
processChannel <- false
close(processChannel)
}()
fmt.Println(<-processChannel) // true
fmt.Println(<-processChannel) // false
fmt.Println(<-processChannel) // false
select {
case x := <-processChannel:
fmt.Println(x) // false
}
}
In your code you are closing processChannel so future receives will return the default value (false). One solution is to use processChannel = nil after closing it because:
A nil channel is never ready for communication.
However in your case appendToFullText is closing both channels when pC == false; as such you should probably just return after doing so (because with both channels closed there is no point in keeping the loop running).
Please note that I have only scanned your code

Data race occurs even if using sync.Mutex in map

How can I solve data race bug in this piece of code? I get an error in processPbxQueueByUniqueID func on l.Remove(frontItem). Do I need to maybe create another struct that contains sync.Mutex and *list.List and then change pbxQueueUniqueIDProcessor.processes to map[string]NewStructWithMutexAndList?
var pbxQueueUnique = newPbxQueueUniqueIDProcessor()
var pbxMutex sync.Mutex
type pbxQueueUniqueIDProcessor struct {
processes map[string]*list.List
}
func newPbxQueueUniqueIDProcessor() *pbxQueueUniqueIDProcessor {
return &pbxQueueUniqueIDProcessor{processes: make(map[string]*list.List)}
}
func (q *pbxQueueUniqueIDProcessor) Add(uniqueID string, item pbxQueueItem) {
pbxMutex.Lock()
defer pbxMutex.Unlock()
_, ok := q.processes[uniqueID]
if !ok {
l := &list.List{}
l.PushBack(item)
q.processes[uniqueID] = l
go processPbxQueueByUniqueID(uniqueID)
return
}
q.processes[uniqueID].PushBack(item)
}
func (q *pbxQueueUniqueIDProcessor) Get(uniqueID string) *list.List {
pbxMutex.Lock()
defer pbxMutex.Unlock()
return q.processes[uniqueID]
}
func (q *pbxQueueUniqueIDProcessor) RemoveFromList(uniqueID string, el *list.Element) {
pbxMutex.Lock()
defer pbxMutex.Unlock()
l := q.processes[uniqueID]
if l == nil {
return
}
l.Remove(el)
}
func processPbxQueueByUniqueID(uniqueID string) {
l := pbxQueueUnique.Get(uniqueID)
if l == nil {
return
}
for {
frontItem := l.Front()
if frontItem == nil {
break
}
frontValue := frontItem.Value.(pbxQueueItem)
execAcc(frontValue)
l.Remove(frontItem)
}
}

go type-switch how to deal with point to nil

like this, if msg point to null value, how to deal with it in clean code
func test(a SomeType) {
switch msg := a.(type) {
case *type1:
dosomething1(a)
case *type2:
dosomething2(a)
case *type3:
dosomething3(a)
default:
do()
}
the main func may be this
func main() {
var b_ptr *stTest
aa := interface{}(b)
test(aa)
}
type test struct {
c int
d string
}
b_ptr is a pointer, but it point to nil, i want to judge it in test func
if i use b_ptr in test func, eg: print a.c or a.d, it will crash.
i do it by this way. do if(), everywhere use a, but it too stupid.
func test(a SomeType) {
switch msg := a.(type) {
case *type1:
if msg == nil {
return
}
dosomething1(a)
case *type2:
if msg == nil {
return
}
dosomething2(a)
case *type3:
if msg == nil {
return
}
dosomething3(a)
default:
do()
}
Generally this ought to be considered a problem with the caller, and therefore it should be the caller's job to deal with the fallout. Passing around non-nil interfaces that hold nil values is bad practice unless it's intentional/expected.
If you can't avoid the non-nil interface with nil-pointer however, then you can use reflect to check if the underlying value is nil.
func test(a SomeType) {
if rv := reflect.ValueOf(a); !rv.IsValid() || rv.IsNil() {
return
}
switch msg := a.(type) {
case *type1:
dosomething1(a)
case *type2:
dosomething2(a)
case *type3:
dosomething3(a)
default:
do()
}
}

iterate through map[string]interface{} recursively using reflect package

i just trying learn to use reflection, i wanna go througe each field in the map[string]interface{} this why i using
json.Unmarshal to interface{}.
func i2s(data interface{}, out interface{}) error {
fmt.Println(reflect.TypeOf(data).Kind())
switch reflect.TypeOf(data).Kind() {
case reflect.Map, reflect.Array, reflect.Ptr, reflect.Slice:
i2s(reflect.TypeOf(data).Elem(), out)
case reflect.Struct:
for i := 0; i < reflect.ValueOf(data).NumField(); i++ {
f := reflect.TypeOf(data).Field(i)
fmt.Println(f.Name, f.Type.Name())
if f.Tag != "" {
fmt.Println(f.Tag)
}
}
}
return nil
}
type Simple struct {
ID int
Username string
Active bool
}
func main() {
test := &Simple{
ID: 42,
Username: "dcandu",
Active: true,
}
jsonRaw, _ := json.Marshal(test)
var tmpData interface{}
json.Unmarshal(jsonRaw, &tmpData)
result := new(Simple)
i2s(tmpData, result)
}
When i run this code i get the infinite loop.
Please tell me where i'm doing wrong.
Thank you.

go: var declared but not used error - how to work around it?

In this function I get "s declared and not used" which I don't understand - do I need to somehow tag it as 'really I used it' or something?
func getString(data map[string]interface{}, name string) (string, error) {
s := data[name]
if reflect.TypeOf(s).Kind() != reflect.String {
return s.(string), nil
}
return "", &apiError{1, "it's not a string"}
}
Oddly, I don't get the error from this function:
func getInt(data map[string]interface{}, name string) (int, error) {
t := data[name]
if reflect.TypeOf(t).Kind() == reflect.Int {
return t.(int), nil
}
return 0, &apiError{1, "it's not an int"}
}
Also, any thoughts on the right way to factor these into a single function would be welcomed!
Your error comes from (declaring and not) using the same identifier elsewhere because this compiles and runs fine on golang.org:
package main
import "reflect"
func main() {
m := make(map[string]interface{})
m["foo"] = "25"
getString(m, "foo")
}
func getString(data map[string]interface{}, name string) (string, error) {
s := data[name]
if reflect.TypeOf(s).Kind() != reflect.String {
return s.(string), nil
}
return "", nil
}
Your code looks correct, error isn't reproducible.
Sure you can refactor these into a single function, but you may not like it depending of tastes.
type VType int
const (
VInteger VType = iota
VString
VUnknown
)
func getValue(data map[string]interface{}, name string) (VType, int, string) {
switch v := data[name].(type) {
case int:
return VInteger, v, ""
case string:
return VString, 0, v
default:
return VUnknown, 0, ""
}
}
func main() {
m := make(map[string]interface{})
m["foo"] = "25"
switch t, i, s := getValue(m, "foo"); t {
case VInteger:
fmt.Println("int ", i) //do something with int
case VString:
fmt.Println("string ", s) //do something with string
case VUnknown:
err := &apiError{1, "it's not an int"} //do something with err
}
}

Resources