Golang中break、continue、goto与Label、for-select的结合使用

824 阅读3分钟

简介

这里介绍下Go中的break、continue、goto的使用,以及与Label的结合使用。同时会介绍下for-select,如何跟break、continue、goto以及Label结合使用。

break

break在循环中的作用是跳出循环。

func testBreak() {
  list := []int{1, 2, 3, 4}
​
  for _, i := range list {
    if i == 2 {
      break
    }
    fmt.Println(i)
  }
}
​
// 运行输出结果:
1

break单独使用只能跳出当前循环,无法跳出多层循环。

func testBreak2() {
   list1 := []int{1, 2, 3}
   list2 := []int{4, 5, 6, 7}
​
   for _, i := range list1 {
      for _, i2 := range list2 {
         if i2 == 5 {
            break // 只会跳出当前for循环,还会执行当前循环下面的语句
         }
      }
      fmt.Println(i)
   }
}
​
// 运行输出结果:
1
2
3

想一次跳出到指定的地方,可以结合Label实现。

func testBreak3() {
   list1 := []int{1, 2, 3}
   list2 := []int{4, 5, 6, 7}
​
Label:
   for _, i := range list1 {
      for _, i2 := range list2 {
         if i2 == 5 {
            break Label
         }
      }
      fmt.Println(i)
   }
   fmt.Println("end")
}
​
// Label在最外层for循环,此时break Label会直接跳出外层for循环
// 运行输出结果:
end

continue

continue在循环中后续语句不会再执行,但不会跳出当前的循环。

func testContinue1() {
   list := []int{1, 2, 3, 4}
​
   for _, i := range list {
      if i == 2 {
         continue // i == 2时,不再执行后续的语句
      }
      fmt.Println(i)
   }
}
​
// 运行输出结果:
1
3
4

如果continue到Label,则表示对Label指定的for循环,continue之后的语句都不再执行。

func testContinue2() {
  list := []int{1, 2, 3, 4}
​
Label:
  for _, i := range list {
    if i == 2 {
      continue Label
    }
    fmt.Println(i)
  }
}
​
// 运行输出结果:
1
3
4

如果多层循环,也是一样,对Lable指定的for循环,continue之后的语句都不再执行。

func testContinue3() {
  list1 := []int{1, 2}
  list2 := []int{3, 4, 5}
​
Label:
  for _, i := range list1 {
    for _, i2 := range list2 {
      if i2 == 4 {
        continue Label // Label指定的最外层循环
      }
      fmt.Println(i, i2) // i==1时输出 1 3, i==2时输出 2 3
    }
​
    fmt.Println(i) // continue Label的逻辑会影响到最外层的逻辑,这里在continue之后,导致这里永远不会执行。
  }
}
​
// 运行输出结果:
1 3
2 3

goto

goto需要结合Label使用,可以直接跳到对应的代码逻辑。

func testGoto() {
  a := 1
  b := 2
​
  if a == 1 {
    goto Label
  }
  fmt.Println(a)
Label:
  fmt.Println(b)
}
​
// 运行输出结果:
2

for-select

golang里的select,用来监听channel的读写事件,当事件发生时,触发相应的动作。

//select基本用法
select {
case <- chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}

select执行后会直接结束,所以一般会跟for循环结合使用。

func testSelect() {
  ticker := time.NewTicker(time.Second)
  defer ticker.Stop()
  for {
    select {
    case <-ticker.C:
      fmt.Println(1)
    }
  }
}
​
// 会一直循环下去
// 运行输出结果:
1
1
...

break

在select中break,并不会跳出for循环。

func testSelectBreak() {
   ticker := time.NewTicker(time.Second)
   defer ticker.Stop()
   for {
      select {
      case <-ticker.C:
         fmt.Println(1)
         break // 无法跳出for循环
      }
   }
}
​
// 会一直循环下去
// 运行输出结果:
1
1
...

break结合标签可以跳到for循环之外

func testSelectBreakLabel() {
   ticker := time.NewTicker(time.Second)
   defer ticker.Stop()
​
Label:
   for {
      select {
      case <-ticker.C:
         fmt.Println(1)
         break Label // 跳出for循环
      }
   }
​
   fmt.Println("end")
}
​
// 运行输出结果:
1
end

goto

在for-select,使用goto结合标签可以跳到for循环之外。与break的区别在于Label标签要在for语句之后。

func testSelectGotoLabel() {
   ticker := time.NewTicker(time.Second)
   defer ticker.Stop()
​
   for {
      select {
      case <-ticker.C:
         fmt.Println(1)
         goto Label // 跳出for循环
      }
   }
​
Label:
   fmt.Println("end")
}
​
// 运行输出结果:
1
end

continue

continue无法在select里单独使用,编译报错,使用的话必须在for里面,使用for-select模式。

func testSelectContinue() {
  ticker := time.NewTicker(time.Second)
  defer ticker.Stop()
​
Label:
  for {
    select {
    case <-ticker.C:
      fmt.Println(1)
      continue Label // 并不会跳出for循环
      fmt.Println(2) // continue之后的语句不会执行
    }
  }
}
​
// 会一直循环下去
// 运行输出结果:
1
1
...