for-select 的小坑

在某佬交流羣看到下面這樣一段代碼。

問,執行下面代碼會出現什麼現象?

func main() {
  ac := make(chan int, 1)
  ac <- 1
  bc := make(chan int, 3)
  bc <- 1
  bc <- 2

  for {
    select {
    case a := <-ac:
      fmt.Println("from ac: ", a)
    case b := <-bc:
      fmt.Println("from bc: ", b)
    default:
      break
    }
  }
  fmt.Println("do other thing")
  fmt.Println("over")
}

go specification

A "break" statement terminates execution of the innermost "for", "switch", or "select" statement within the same function.

也就是說在 select 中,break 會中止其後面語句的執行,但是並不會打斷外層的 for 循環,所以上面的代碼會打印完 chan 中數據後一直陷入死循環,那麼解決方法有哪些呢?

continue

A "continue" statement begins the next iteration of the innermost "for" loop at its post statement. The "for" loop must be within the same function.

continue 語句會結束最內層的 for 循環。

for {
  select {
  case a := <-ac:
    fmt.Println("from ac: ", a)
    continue
  case b := <-bc:
    fmt.Println("from bc: ", b)
    continue
  default:
  }
  break
}

break label

If there is a label, it must be that of an enclosing "for", "switch", or "select" statement, and that is the one whose execution terminates.

break 可以打斷 for,select,switch 代碼標籤的運作,可以通過加個標籤來退出循環。

forSelectLoop:
  for {
    select {
    case a := <-ac:
      fmt.Println("from ac: ", a)
    case b := <-bc:
      fmt.Println("from bc: ", b)
    default:
      break forSelectLoop
    }
  }

goto

可以通過 goto 跳出循環。

for {
    select {
    case a := <-ac:
      fmt.Println("from ac: ", a)
    case b := <-bc:
      fmt.Println("from bc: ", b)
    default:
      goto over
    }
  }

over:
  fmt.Println("do other thing")
  fmt.Println("over")

other

可以在 for 循環條件處做些標記。

var sign bool
  for !sign {
    select {
    case a := <-ac:
      fmt.Println("from ac: ", a)
    case b := <-bc:
      fmt.Println("from bc: ", b)
    default:
      sign = true
    }
  }

Reference

# https://go.dev/ref/spec#Break_statements

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ALR8WzzUkEfCQ3sD28lr9w