30 range下元素copy #
range介绍
range
可作用于 string
, array
, 指针数组
, slice
, map
, 接收chan
.
复习下range的写法:
s := []string{"a", "b", "c"}
for i, v := range s {
fmt.Printf("index=%d, value=%s\n", i, v)
}
for _, v := range s {
fmt.Printf("value=%s\n", v)
}
for i := range s {
fmt.Printf("index=%d\n", i)
}
output:
index=0, value=a
index=1, value=b
index=2, value=c
value=a
value=b
value=c
index=0
index=1
index=2
range下值类型
accounts := []account{{balance: 100.}, {balance: 200.}, {balance: 300.}}
for _, a := range accounts {
a.balance += 1000
}
fmt.Println(accounts)
[{100} {200} {300}]
因为我们修改的是拷贝出来的a 原来的slice没有改变到
下面才是能改变的
for i := range accounts {
accounts[i].balance += 1000
}
或者
for i := 0; i < len(accounts); i++ {
accounts[i].balance += 1000
}
那么这种改法呢
accounts := []*account{}
作者不是很赞同,在#91
时会提及
31 range下元素计算 #
slice
s在range时被拷贝
func sliceCopy() {
s := []int{0, 1, 2}
for range s {
s = append(s, 10)
}
fmt.Println(s)
}
[0 1 2 10 10 10]
陷入死循环,因为s每次迭代都是重新求值
func sliceNeverStop() {
s := []int{0, 1, 2}
for i := 0; i < len(s); i++ {
s = append(s, 10)
}
fmt.Println(s)
}
channel 在range时拷贝值,类似slice
func chanCopy() {
ch1 := make(chan int, 3)
ch2 := make(chan int, 3)
go func() {
ch1 <- 1
ch1 <- 11
ch1 <- 111
close(ch1)
}()
go func() {
ch2 <- 2
ch2 <- 22
ch2 <- 222
close(ch2)
}()
ch := ch1
for v := range ch {
fmt.Println(v) // 保持ch1的来源,copy in range first time
// 此时v的来源是ch1的值
ch = ch2
}
fmt.Println("---")
for v := range ch { // ch 还是被ch2 赋值了
fmt.Println(v)
}
}
output
1
11
111
---
2
22
222
array 在range时发生拷贝
func arrayCopy() {
arr := [3]int{0, 1, 2}
for i, v := range arr {
arr[2] = 10
if i == 2 {
fmt.Println(v)
}
}
fmt.Println(arr)
}
output
2
[0 1 10]
array 指针指向源数据
func arrayCopy() {
arr := [3]int{0, 1, 2}
for i, v := range &arr { //此时为指针
arr[2] = 10
if i == 2 {
fmt.Println(v)
}
}
fmt.Println(arr)
}
output
10
[0 1 10]
32 range下指针元素 #
range时临时变量指向同个指针
type Customer struct {
ID string
Balance float64
}
type Store struct{ m map[string]*Customer }
func (s *Store) storeCustomers(customers []Customer) {
for _, customer := range customers {
// 此时customer 在每次迭代中都是同一指针
s.m[customer.ID] = &customer
}
}
func rangeLast() {
s := Store{
m: map[string]*Customer{},
}
s.storeCustomers([]Customer{{ID: "1", Balance: 10}, {ID: "2", Balance: -10}, {ID: "3", Balance: 0}})
for _, v := range s.m {
fmt.Println(v)
}
}
&{3 0}
&{3 0}
&{3 0}
化解
func (s *Store) storeCustomers(customers []Customer) {
for _, customer := range customers {
customer1 := customer
s.m[customer.ID] = &customer1
}
}
output
&{1 10}
&{2 -10}
&{3 0}
33 map迭代陷阱 #
map的迭代无序性
在同一迭代中操作元素
func rangeMap() {
m := map[int]bool{
0: true, 1: false, 2: true,
}
for k, v := range m {
if v {
m[10+k] = true
}
}
fmt.Println(m)
}
无序的体现,在map range中改变map,每次生成都是无序.
建议用新变量m2来存储赋值结果.
34 break工作机制 #
break只终结当前循环
func breakCurr() {
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i)
switch i {
default:
case 2:
break
}
}
}
output
0 1 2 3 4
化解
func breakCurr() {
loopLabel:
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i)
switch i {
default:
case 2:
break loopLabel
}
}
}
output
0 1 2
例子2
func breakFor() {
ch := make(chan int, 3)
ctx, canF := context.WithCancel(context.Background())
go func() {
time.Sleep(1 * time.Second)
canF()
}()
for {
select {
case <-ch:
fmt.Println("from ch")
case <-ctx.Done():
// Do something case <-ctx.Done():
fmt.Println("wanna break for and exit")
break
}
}
}
以上函数无法中终结,只break select
同样修复方式:
func breakFor() {
ch := make(chan int, 3)
ctx, canF := context.WithCancel(context.Background())
go func() {
time.Sleep(1 * time.Second)
canF()
}()
loopLabel:
for {
select {
case <-ch:
fmt.Println("from ch")
break
case <-ctx.Done():
// Do something case <-ctx.Done():
fmt.Println("wanna break for and exit")
break loopLabel
}
}
fmt.Println("im arrive")
}
output
wanna break for and exit
im arrive
35 在for中使用defer #
defer的执行是推入函数的调用栈,在函数return时才会执行.
如果在for循环中打开文件,defer关闭文件句柄的行为存在风险
建议其一,将打开文件,defer关闭文件放在一个函数,在for中调用单独函数,保证defer的执行