目录

range语句

range slice

func main() {
	s := []int{1, 2, 3}
	for i := 0; i < len(s); i++ {
		if i == 1 {
			s = append(s, 4)
		}
		fmt.Println(i, s[i])
	}
}

上面的代码,遍历中新增元素是没有问题的。 但是下面的range遍历,结果是不一样的:

func main() {
	s := []int{1, 2, 3}
	fmt.Println(s) //[1 2 3]
	for i, v := range s {
		if i == 0 {
			s = append(s, 4)
		}
		fmt.Println(s) //[1 2 3 4]
		fmt.Println(i, v)
	}
	fmt.Println(s) //[1 2 3 4]
}

这里在range中增加元素,并没有改变range的次数。 其实这里只是一个语法糖。 对于切片的for…range底层源码是这样的:

//   for_temp := range
//   len_temp := len(for_temp)
//   for index_temp = 0; index_temp < len_temp; index_temp++ {
//           value_temp = for_temp[index_temp]
//           index = index_temp
//           value = value_temp
//           original body
//   }

可以看到,在遍历之前,就取到了切片的长度。后面增删元素,并不影响遍历的次数。

仅仅不会改变遍历的次数,但是切片里的数据是会改动的,所以不要在遍历的时候删除数据。

range map

// Lower a for range over a map.
// The loop we generate:
//   var hiter map_iteration_struct
//   for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
//           index_temp = *hiter.key
//           value_temp = *hiter.val
//           index = index_temp
//           value = value_temp
//           original body
//   }
func mtest() {
	m := make(map[int]int)
	m[0] = 1
	m[1] = 2
	var count int
	for k, v := range m {
		fmt.Println(k, v)
		count++
		if count == 1 {
			m[3] = 3
			fmt.Println("add")
		}
	}
}

输出:

0 1
add
1 2
3 3

可以看出,range过程中增加元素是会改变range次数的。实验证明,删除元素也有同样的效果。

range channel

// Lower a for range over a channel.
// The loop we generate:
//   for {
//           index_temp, ok_temp = <-range
//           if !ok_temp {
//                   break
//           }
//           index = index_temp
//           original body
//   }

range array

// Lower a for range over an array.
// The loop we generate:
//   len_temp := len(range)
//   range_temp := range
//   for index_temp = 0; index_temp < len_temp; index_temp++ {
//           value_temp = range_temp[index_temp]
//           index = index_temp
//           value = value_temp
//           original body
//   }

可以看到输入值和slice类似,都是提前取到长度。

range string

// Lower a for range over a string.
// The loop we generate:
//   len_temp := len(range)
//   var next_index_temp int
//   for index_temp = 0; index_temp < len_temp; index_temp = next_index_temp {
//           value_temp = rune(range[index_temp])
//           if value_temp < utf8.RuneSelf {
//                   next_index_temp = index_temp + 1
//           } else {
//                   value_temp, next_index_temp = decoderune(range, index_temp)
//           }
//           index = index_temp
//           value = value_temp
//           // original body
//   }
func stringtest() {
	s := "abcd"
	var count int
	for _, v := range s {
		fmt.Printf("%c\n", v)
		count++
		if count == 1 {
			s = s + "ss"
			fmt.Println("add")
		}
	}
	fmt.Println(s)
}

验证的结果为range过程中对字符串的追加及截取,都不影响range。