Go语言错误总结(四)

22、内建的数据结构操作不是同步的

即使Go本身有很多特性来支持并发,并发安全的数据集合并不是其中之一,确保数据集合以原子的方式更新是你的职责。Goroutines和channels是实现这些原子操作的推荐方式,但你也可以使用“sync”包,如果它对你的应用有意义的话。

23、String在“range”语句中的迭代值

索引值(“range”操作返回的第一个值)是返回的第二个值的当前“字符”(unicode编码的point/rune)的第一个byte的索引。它不是当前“字符”的索引,这与其他语言不同。注意真实的字符可能会由多个rune表示。如果你需要处理字符,确保你使用了“norm”包(golang.org/x/text/unicode/norm)。

string变量的for range语句将会尝试把数据翻译为UTF8文本。对于它无法理解的任何byte序列,它将返回0xfffd runes(即unicode替换字符),而不是真实的数据。如果你任意(非UTF8文本)的数据保存在string变量中,确保把它们转换为byte slice,以得到所有保存的数据。

package main
import "fmt"
func main() {    data := "A\xfe\x02\xff\x04"    for _, v := range data {        fmt.Printf("%#x ", v)    }    //prints: 0x41 0xfffd 0x2 0xfffd 0x4 (not ok)    fmt.Println()    for _, v := range []byte(data) {        fmt.Printf("%#x ", v)    }    //prints: 0x41 0xfe 0x2 0xff 0x4 (good)}

运行结果:​​​​​​​

0x41 0xfffd 0x2 0xfffd 0x4 0x41 0xfe 0x2 0xff 0x4 

24、对Map使用“for range”语句迭代

如果你希望以某个顺序(比如,按key值排序)的方式得到元素,就需要这个技巧。每次的map迭代将会生成不同的结果。Go的runtime有心尝试随机化迭代顺序,但并不总会成功,这样你可能得到一些相同的map迭代结果。所以如果连续看到5个相同的迭代结果,不要惊讶。

package main
import "fmt"
func main() {    m := map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}    for k, v := range m {        fmt.Println(k, v)    }}

而且如果你使用Go的游乐场(https://play.golang.org/),你将总会得到同样的结果,因为除非你修改代码,否则它不会重新编译代码。

25、"switch"声明中的失效行为

在“switch”声明语句中的“case”语句块在默认情况下会break。这和其他语言中的进入下一个“next”代码块的默认行为不同。​​​​​​​

package main
import "fmt"
func main() {    isSpace := func(ch byte) bool {        switch ch {        case ' ':        case '\t':            return true        }        return false    }    fmt.Println(isSpace('\t'))    fmt.Println(isSpace(' '))}

运行结果:​​​​​​​

truefalse

你可以通过在每个“case”块的结尾使用“fallthrough”,来强制“case”代码块进入。你也可以重写switch语句,来使用“case”块中的表达式列表。​​​​​​​

package main
import "fmt"
func main() {    isSpace := func(ch byte) bool {        switch ch {        case ' ':            fallthrough        case '\t':            return true        }        return false    }    fmt.Println(isSpace('\t'))    fmt.Println(isSpace(' '))}​​​​​​​
package main
import "fmt"
func main() {    isSpace := func(ch byte) bool {        switch ch {        case ' ', '\t':            return true        }        return false    }    fmt.Println(isSpace('\t'))    fmt.Println(isSpace(' '))}

运行结果:​​​​​​​

truetrue

26、自增和自减

许多语言都有自增和自减操作。不像其他语言,Go不支持前置版本的操作。你也无法在表达式中使用这两个操作符。

错误代码:​​​​​​​

package main
import (    "fmt")
func main() {    data := []int{1, 2, 3}    i := 0    ++i    fmt.Println(data[i++])}

编译错误:

 
./main.go:10:2: syntax error: unexpected ++, expecting }

正确代码:​​​​​​

package main
import "fmt"
func main() {    data := []int{1, 2, 3}    i := 0    i++    fmt.Println(data[i])}

27、按位NOT操作

许多语言使用 ~作为一元的NOT操作符(即按位补足),但Go为了这个重用了XOR操作符(^)。

错误代码:​​​​​​​

package main
import "fmt"
func main() {    fmt.Println(~2)}

编译错误:

 
./main.go:6:14: bitwise complement operator is ^

正确代码:​​​​​​​

package main
import "fmt"
func main() {    var d uint8 = 2    fmt.Printf("%08b\n", ^d)}

Go依旧使用^作为XOR的操作符,这可能会让一些人迷惑。

如果你愿意,你可以使用一个二元的XOR操作(如, 0x02 XOR 0xff)来表示一个一元的NOT操作(如,NOT 0x02)。这可以解释为什么^被重用来表示一元的NOT操作。

Go也有特殊的‘AND NOT’按位操作(&^),这也让NOT操作更加的让人迷惑。这看起来需要特殊的特性/hack来支持 A AND (NOT B),而无需括号。​​​​​​​

package main
import "fmt"
func main() {    var a uint8 = 0x82    var b uint8 = 0x02    fmt.Printf("%08b [A]\n", a)    fmt.Printf("%08b [B]\n", b)    fmt.Printf("%08b (NOT B)\n", ^b)    fmt.Printf("%08b ^ %08b = %08b [B XOR 0xff]\n", b, 0xff, b^0xff)    fmt.Printf("%08b ^ %08b = %08b [A XOR B]\n", a, b, a^b)    fmt.Printf("%08b & %08b = %08b [A AND B]\n", a, b, a&b)    fmt.Printf("%08b &^%08b = %08b [A 'AND NOT' B]\n", a, b, a&^b)    fmt.Printf("%08b&(^%08b)= %08b [A AND (NOT B)]\n", a, b, a&(^b))}

28、操作优先级的差异

除了”bit clear“操作(&^),Go也一个与许多其他语言共享的标准操作符的集合。尽管操作优先级并不总是一样。​​​​​​​

package main
import "fmt"
func main() {    fmt.Printf("0x2 & 0x2 + 0x4 -> %#x\n", 0x2&0x2+0x4)    //prints: 0x2 & 0x2 + 0x4 -> 0x6    //Go:    (0x2 & 0x2) + 0x4    //C++:    0x2 & (0x2 + 0x4) -> 0x2    fmt.Printf("0x2 + 0x2 << 0x1 -> %#x\n", 0x2+0x2<<0x1)    //prints: 0x2 + 0x2 << 0x1 -> 0x6    //Go:     0x2 + (0x2 << 0x1)    //C++:   (0x2 + 0x2) << 0x1 -> 0x8    fmt.Printf("0xf | 0x2 ^ 0x2 -> %#x\n", 0xf|0x2^0x2)    //prints: 0xf | 0x2 ^ 0x2 -> 0xd    //Go:    (0xf | 0x2) ^ 0x2    //C++:    0xf | (0x2 ^ 0x2) -> 0xf}

运行结果:​​​​​​​

0x2 & 0x2 + 0x4 -> 0x60x2 + 0x2 << 0x1 -> 0x60xf | 0x2 ^ 0x2 -> 0xd
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值