初级篇:1-35
1. 左大括号 {
一般不能单独放一行
2. 未使用的变量
3. 未使用的 import
4. 简短声明的变量只能在函数内部使用
5. 使用简短声明来重复声明变量
6. 不能使用简短声明来设置字段的值
7. 不小心覆盖了变量
8. 显式类型的变量无法使用 nil 来初始化
9. 直接使用值为 nil 的 slice、map
10. map 容量
11. string 类型的变量值不能为 nil
php 从数据库提取二进制图片的处理代码
pdf0星超过10%的资源28KB
下载
12. Array 类型的值作为函数参数
13. range 遍历 slice 和 array 时混淆了返回值
14. slice 和 array 其实是一维数据
data-channel:将数据通道转换为流
zip0星超过10%的资源3KB
下载
15. 访问 map 中不存在的 key
16. string 类型的值是常量,不可更改
17. string 与 byte slice 之间的转换
> php -r '$name="中文"; var_dump($name);' # "中文" 占用 6 个字节string(6) "中文"> php -r '$name="中文"; var_dump($name[0]);' # 把第一个字节当做 Unicode 字符读取,显示 U+FFFDstring(1) "�" > php -r '$name="中文"; var_dump($name[0].$name[1].$name[2]);'string(3) "中"
func main() {x := "ascii"fmt.Println(x[0]) // 97fmt.Printf("%T\n", x[0])// uint8}
func main() {str1 := "ABC"fmt.Println(utf8.ValidString(str1)) // truestr2 := "A\xfeC"fmt.Println(utf8.ValidString(str2)) // falsestr3 := "A\\xfeC"fmt.Println(utf8.ValidString(str3)) // true // 把转义字符转义成字面值}
data = u'♥' print(len(data)) # 1
func main() {char := "♥"fmt.Println(len(char)) // 3}
func main() {char := "♥"fmt.Println(utf8.RuneCountInString(char)) // 1}
func main() {char := "é"fmt.Println(len(char)) // 3fmt.Println(utf8.RuneCountInString(char)) // 2fmt.Println("cafe\u0301") // café // 法文的 cafe,实际上是两个 rune 的组合}
func main() {x := []int {1,2 // syntax error: unexpected newline, expecting comma or }}y := []int{1,2,}z := []int{1,2}// ...}
func main() {log.Fatal("Fatal level log: log entry") // 输出信息后,程序终止执行log.Println("Nomal level log: log entry")}
func main() {data := "A\xfe\x02\xff\x04"for _, v := range data {fmt.Printf("%#x ", v) // 0x41 0xfffd 0x2 0xfffd 0x4 // 错误}for _, v := range []byte(data) {fmt.Printf("%#x ", v) // 0x41 0xfe 0x2 0xff 0x4 // 正确}}
func main() {m := map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}for k, v := range m {fmt.Println(k, v)}}
func main() {isSpace := func(char byte) bool {switch char {case ' ': // 空格符会直接 break,返回 false // 和其他语言不一样// fallthrough // 返回 truecase '\t':return true}return false}fmt.Println(isSpace('\t')) // truefmt.Println(isSpace(' ')) // false}
func main() {isSpace := func(char byte) bool {switch char {case ' ', '\t':return true}return false}fmt.Println(isSpace('\t')) // truefmt.Println(isSpace(' ')) // true}
// 错误示例func main() {data := []int{1, 2, 3}i := 0++i // syntax error: unexpected ++, expecting }fmt.Println(data[i++]) // syntax error: unexpected ++, expecting :}// 正确示例func main() {data := []int{1, 2, 3}i := 0i++fmt.Println(data[i]) // 2}
// 错误的取反操作func main() {fmt.Println(~2) // bitwise complement operator is ^}// 正确示例func main() {var d uint8 = 2fmt.Printf("%08b\n", d) // 00000010fmt.Printf("%08b\n", ^d) // 11111101}
func main() {var a uint8 = 0x82var b uint8 = 0x02fmt.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))}
10000010 [A]00000010 [B]11111101 (NOT B)00000010 ^ 11111111 = 11111101 [B XOR 0xff]10000010 ^ 00000010 = 10000000 [A XOR B]10000010 & 00000010 = 00000010 [A AND B]10000010 &^00000010 = 10000000 [A 'AND NOT' B]10000010&(^00000010)= 10000000 [A AND (NOT B)]
Precedence Operator5 * / % << >> & &^4 + - | ^3 == != < <= > >=2 &&1 ||
func main() {in := MyData{1, "two"}fmt.Printf("%#v\n", in) // main.MyData{One:1, two:"two"}encoded, _ := json.Marshal(in)fmt.Println(string(encoded)) // {"One":1} // 私有字段 two 被忽略了var out MyDatajson.Unmarshal(encoded, &out)fmt.Printf("%#v\n", out) // main.MyData{One:1, two:""}}
// 主程序会直接退出func main() {workerCount := 2for i := 0; i < workerCount; i++ {go doIt(i)}time.Sleep(1 * time.Second)fmt.Println("all done!")}func doIt(workerID int) {fmt.Printf("[%v] is running\n", workerID)time.Sleep(3 * time.Second) // 模拟 goroutine 正在执行 fmt.Printf("[%v] is done\n", workerID)}
// www.topgoer.com go语言中文文档// 等待所有 goroutine 执行完毕// 进入死锁func main() {var wg sync.WaitGroupdone := make(chan struct{})workerCount := 2for i := 0; i < workerCount; i++ {wg.Add(1)go doIt(i, done, wg)}close(done)wg.Wait()fmt.Println("all done!")}func doIt(workerID int, done <-chan struct{}, wg sync.WaitGroup) {fmt.Printf("[%v] is running\n", workerID)defer wg.Done()<-donefmt.Printf("[%v] is done\n", workerID)}
// www.topgoer.com go语言中文文档// 等待所有 goroutine 执行完毕// 使用传址方式为 WaitGroup 变量传参// 使用 channel 关闭 goroutinefunc main() {var wg sync.WaitGroupdone := make(chan struct{})ch := make(chan interface{})workerCount := 2for i := 0; i < workerCount; i++ {wg.Add(1)go doIt(i, ch, done, &wg) // wg 传指针,doIt() 内部会改变 wg 的值}for i := 0; i < workerCount; i++ { // 向 ch 中发送数据,关闭 goroutinech <- i}close(done)wg.Wait()close(ch)fmt.Println("all done!")}func doIt(workerID int, ch <-chan interface{}, done <-chan struct{}, wg *sync.WaitGroup) {fmt.Printf("[%v] is running\n", workerID)defer wg.Done()for {select {case m := <-ch:fmt.Printf("[%v] m => %v\n", workerID, m)case <-done:fmt.Printf("[%v] is done\n", workerID)return}}}
func main() {ch := make(chan string)go func() {for m := range ch {fmt.Println("Processed:", m)time.Sleep(1 * time.Second) // 模拟需要长时间运行的操作}}()ch <- "cmd.1"ch <- "cmd.2" // 不会被接收处理}
func main() {ch := make(chan int)done := make(chan struct{})for i := 0; i < 3; i++ {go func(idx int) {select {case ch <- (idx + 1) * 2:fmt.Println(idx, "Send result")case <-done:fmt.Println(idx, "Exiting")}}(i)}fmt.Println("Result: ", <-ch)close(done)time.Sleep(3 * time.Second)}
func main() {var ch chan int // 未初始化,值为 nilfor i := 0; i < 3; i++ {go func(i int) {ch <- i}(i)}fmt.Println("Result: ", <-ch)time.Sleep(2 * time.Second)}
func main() {inCh := make(chan int)outCh := make(chan int)go func() {var in <-chan int = inChvar out chan<- intvar val intfor {select {case out <- val:println("--------")out = nilin = inChcase val = <-in:println("++++++++++")out = outChin = nil}}}()go func() {for r := range outCh {fmt.Println("Result: ", r)}}()time.Sleep(0)inCh <- 1inCh <- 2time.Sleep(3 * time.Second)}
type data struct {num intkey *stringitems map[string]bool}func (this *data) pointerFunc() {this.num = 7}func (this data) valueFunc() {this.num = 8*this.key = "valueFunc.key"this.items["valueFunc"] = true}func main() {key := "key1"d := data{1, &key, make(map[string]bool)}fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items)d.pointerFunc() // 修改 num 的值为 7fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items)d.valueFunc() // 修改 key 和 items 的值fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items)}
1.1.2. 中级篇:36-51
// 请求失败造成 panicfunc main() {resp, err := http.Get("https://api.ipify.org?format=json")defer resp.Body.Close() // resp 可能为 nil,不能读取 Bodyif err != nil {fmt.Println(err)return}body, err := ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(body))}func checkError(err error) {if err != nil{log.Fatalln(err)}}
// 大多数情况正确的示例func main() {resp, err := http.Get("https://api.ipify.org?format=json")checkError(err)defer resp.Body.Close() // 绝大多数情况下的正确关闭方式body, err := ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(body))}
Get https://api.ipify.org?format=...: x509: certificate signed by unknown authority
可以直接在处理 HTTP 响应错误的代码块中,直接关闭非 nil 的响应体。
手动调用 defer 来关闭响应体:
// 正确示例func main() {resp, err := http.Get("http://www.baidu.com")// 关闭 resp.Body 的正确姿势if resp != nil {defer resp.Body.Close()}checkError(err)defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(body))}
_, err = io.Copy(ioutil.Discard, resp.Body) // 手动丢弃读取完毕的数据
json.NewDecoder(resp.Body).Decode(&data)
直接设置请求变量的 Close 字段值为 true,每次请求结束后就会主动关闭连接。
设置 Header 请求头部选项 Connection: close,然后服务器返回的响应头部也会有这个选项,此时 HTTP 标准库会主动断开连接。
// 主动关闭连接func main() {req, err := http.NewRequest("GET", "http://golang.org", nil)checkError(err)req.Close = true//req.Header.Add("Connection", "close") // 等效的关闭方式resp, err := http.DefaultClient.Do(req)if resp != nil {defer resp.Body.Close()}checkError(err)body, err := ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(body))}
func main() {tr := http.Transport{DisableKeepAlives: true}client := http.Client{Transport: &tr}resp, err := client.Get("https://golang.google.cn/")if resp != nil {defer resp.Body.Close()}checkError(err)fmt.Println(resp.StatusCode) // 200body, err := ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(len(string(body)))}
若你的程序要向同一服务器发大量请求,使用默认的保持长连接。
若你的程序要连接大量的服务器,且每台服务器只请求一两次,那收到请求后直接关闭连接。或增加最大文件打开数 fs.file-max 的值。
func main() {var data = []byte(`{"status": 200}`)var result map[string]interface{}if err := json.Unmarshal(data, &result); err != nil {log.Fatalln(err)}fmt.Printf("%T\n", result["status"]) // float64var status = result["status"].(int) // 类型断言错误fmt.Println("Status value: ", status)}
将 int 值转为 float 统一使用
将 decode 后需要的 float 值转为 int 使用
// 将 decode 的值转为 int 使用func main() {var data = []byte(`{"status": 200}`)var result map[string]interface{}if err := json.Unmarshal(data, &result); err != nil {log.Fatalln(err)}var status = uint64(result["status"].(float64))fmt.Println("Status value: ", status)}
使用 Decoder 类型来 decode JSON 数据,明确表示字段的值类型
// 指定字段类型func main() {var data = []byte(`{"status": 200}`)var result map[string]interface{}var decoder = json.NewDecoder(bytes.NewReader(data))decoder.UseNumber()if err := decoder.Decode(&result); err != nil {log.Fatalln(err)}var status, _ = result["status"].(json.Number).Int64()fmt.Println("Status value: ", status)}// 你可以使用 string 来存储数值数据,在 decode 时再决定按 int 还是 float 使用// 将数据转为 decode 为 stringfunc main() {var data = []byte({"status": 200})var result map[string]interface{}var decoder = json.NewDecoder(bytes.NewReader(data))decoder.UseNumber()if err := decoder.Decode(&result); err != nil {log.Fatalln(err)}var status uint64err := json.Unmarshal([]byte(result["status"].(json.Number).String()), &status);checkError(err)fmt.Println("Status value: ", status)}
// struct 中指定字段类型func main() {var data = []byte(`{"status": 200}`)var result struct {Status uint64 `json:"status"`}err := json.NewDecoder(bytes.NewReader(data)).Decode(&result)checkError(err)fmt.Printf("Result: %+v", result)}
可以使用 struct 将数值类型映射为 json.RawMessage 原生数据类型 适用于如果 JSON 数据不着急 decode 或 JSON 某个字段的值类型不固定等情况:
// 状态名称可能是 int 也可能是 string,指定为 json.RawMessage 类型func main() {records := [][]byte{[]byte(`{"status":200, "tag":"one"}`),[]byte(`{"status":"ok", "tag":"two"}`),}for idx, record := range records {var result struct {StatusCode uint64StatusName stringStatus json.RawMessage `json:"status"`Tag string `json:"tag"`}err := json.NewDecoder(bytes.NewReader(record)).Decode(&result)checkError(err)var name stringerr = json.Unmarshal(result.Status, &name)if err == nil {result.StatusName = name}var code uint64err = json.Unmarshal(result.Status, &code)if err == nil {result.StatusCode = code}fmt.Printf("[%v] result => %+v\n", idx, result)}
type data struct {num intfp float32complex complex64str stringchar runeyes boolevents <-chan stringhandler interface{}ref *byteraw [10]byte}func main() {v1 := data{}v2 := data{}fmt.Println("v1 == v2: ", v1 == v2) // true}
type data struct {num intchecks [10]func() bool // 无法比较doIt func() bool // 无法比较m map[string]string // 无法比较bytes []byte // 无法比较}func main() {v1 := data{}v2 := data{}fmt.Println("v1 == v2: ", v1 == v2)}
// 比较相等运算符无法比较的元素func main() {v1 := data{}v2 := data{}fmt.Println("v1 == v2: ", reflect.DeepEqual(v1, v2)) // truem1 := map[string]string{"one": "a", "two": "b"}m2 := map[string]string{"two": "b", "one": "a"}fmt.Println("v1 == v2: ", reflect.DeepEqual(m1, m2)) // trues1 := []int{1, 2, 3}s2 := []int{1, 2, 3}// 注意两个 slice 相等,值和顺序必须一致fmt.Println("v1 == v2: ", reflect.DeepEqual(s1, s2)) // true}
func main() {var b1 []byte = nilb2 := []byte{}fmt.Println("b1 == b2: ", reflect.DeepEqual(b1, b2)) // false}
DeepEqual() 并不总适合于比较 slice
func main() {var str = "one"var in interface{} = "one"fmt.Println("str == in: ", reflect.DeepEqual(str, in)) // truev1 := []string{"one", "two"}v2 := []string{"two", "one"}fmt.Println("v1 == v2: ", reflect.DeepEqual(v1, v2)) // falsedata := map[string]interface{}{"code": 200,"value": []string{"one", "two"},}encoded, _ := json.Marshal(data)var decoded map[string]interface{}json.Unmarshal(encoded, &decoded)fmt.Println("data == decoded: ", reflect.DeepEqual(data, decoded)) // false}
reflect.DeepEqual() 认为空 slice 与 nil slice 并不相等,但注意 byte.Equal() 会认为二者相等:
func main() {var b1 []byte = nilb2 := []byte{}// b1 与 b2 长度相等、有相同的字节序// nil 与 slice 在字节上是相同的fmt.Println("b1 == b2: ", bytes.Equal(b1, b2)) // true}
// 错误的 recover 调用示例func main() {recover() // 什么都不会捕捉panic("not good") // 发生 panic,主程序退出recover() // 不会被执行println("ok")}// 正确的 recover 调用示例func main() {defer func() {fmt.Println("recovered: ", recover())}()panic("not good")}
// 错误的调用示例func main() {defer func() {doRecover()}()panic("not good")}func doRecover() {fmt.Println("recobered: ", recover())}
func main() {data := []int{1, 2, 3}for _, v := range data { v *= 10 // data 中原有元素是不会被修改的 }fmt.Println("data: ", data) // data: [1 2 3]}
func main() {data := []int{1, 2, 3}for i, v := range data { data[i] = v * 10 }fmt.Println("data: ", data) // data: [10 20 30]}
func main() {data := []*struct{ num int }{{1}, {2}, {3},} for _, v := range data { v.num *= 10 // 直接使用指针更新 } fmt.Println(data[0], data[1], data[2]) // &{10} &{20} &{30}}
func get() []byte {raw := make([]byte, 10000)fmt.Println(len(raw), cap(raw), &raw[0]) // 10000 10000 0xc420080000return raw[:3] // 重新分配容量为 10000 的 slice}func main() {data := get()fmt.Println(len(data), cap(data), &data[0]) // 3 10000 0xc420080000}
func get() (res []byte) {raw := make([]byte, 10000)fmt.Println(len(raw), cap(raw), &raw[0]) // 10000 10000 0xc420080000res = make([]byte, 3)copy(res, raw[:3])return}func main() {data := get()fmt.Println(len(data), cap(data), &data[0]) // 3 3 0xc4200160b8}
// 错误使用 slice 的拼接示例func main() {path := []byte("AAAA/BBBBBBBBB")sepIndex := bytes.IndexByte(path, '/') // 4println(sepIndex)dir1 := path[:sepIndex]dir2 := path[sepIndex+1:]println("dir1: ", string(dir1)) // AAAAprintln("dir2: ", string(dir2)) // BBBBBBBBBdir1 = append(dir1, "suffix"...)println("current path: ", string(path)) // AAAAsuffixBBBBpath = bytes.Join([][]byte{dir1, dir2}, []byte{'/'})println("dir1: ", string(dir1)) // AAAAsuffixprintln("dir2: ", string(dir2)) // uffixBBBBprintln("new path: ", string(path)) // AAAAsuffix/uffixBBBB // 错误结果}
重新分配新的 slice 并拷贝你需要的数据
使用完整的 slice 表达式:input[low:high:max],容量便调整为 max - low
// 使用 full slice expressionfunc main() {path := []byte("AAAA/BBBBBBBBB")sepIndex := bytes.IndexByte(path, '/') // 4dir1 := path[:sepIndex:sepIndex] // 此时 cap(dir1) 指定为4, 而不是先前的 16dir2 := path[sepIndex+1:]dir1 = append(dir1, "suffix"...)path = bytes.Join([][]byte{dir1, dir2}, []byte{'/'})println("dir1: ", string(dir1)) // AAAAsuffixprintln("dir2: ", string(dir2)) // BBBBBBBBBprintln("new path: ", string(path)) // AAAAsuffix/BBBBBBBBB}
// 超过容量将重新分配数组来拷贝值、重新存储func main() {s1 := []int{1, 2, 3}fmt.Println(len(s1), cap(s1), s1) // 3 3 [1 2 3 ]s2 := s1[1:]fmt.Println(len(s2), cap(s2), s2) // 2 2 [2 3]for i := range s2 {s2[i] += 20}// 此时的 s1 与 s2 是指向同一个底层数组的fmt.Println(s1) // [1 22 23]fmt.Println(s2) // [22 23]s2 = append(s2, 4) // 向容量为 2 的 s2 中再追加元素,此时将分配新数组来存for i := range s2 {s2[i] += 10}fmt.Println(s1) // [1 22 23] // 此时的 s1 不再更新,为旧数据fmt.Println(s2) // [32 33 14]}
// 定义 Mutex 的自定义类型type myMutex sync.Mutexfunc main() {var mtx myMutexmtx.Lock()mtx.UnLock()}
// 类型以字段形式直接嵌入type myLocker struct {sync.Mutex}func main() {var locker myLockerlocker.Lock()locker.Unlock()}
type myLocker sync.Lockerfunc main() {var locker myLockerlocker.Lock()locker.Unlock()}
// break 配合 label 跳出指定代码块func main() {loop:for {switch {case true:fmt.Println("breaking out...")//break // 死循环,一直打印 breaking out...break loop}}fmt.Println("out...")}
func main() {data := []string{"one", "two", "three"}for _, v := range data {go func() {fmt.Println(v)}()}time.Sleep(3 * time.Second)// 输出 three three three}
func main() {data := []string{"one", "two", "three"}for _, v := range data {vCopy := vgo func() {fmt.Println(vCopy)}()}time.Sleep(3 * time.Second)// 输出 one two three}
func main() {data := []string{"one", "two", "three"}for _, v := range data {go func(in string) {fmt.Println(in)}(v)}time.Sleep(3 * time.Second)// 输出 one two three}
type field struct {name string}func (p *field) print() {fmt.Println(p.name)}// 错误示例func main() {data := []field{{"one"}, {"two"}, {"three"}}for _, v := range data {go v.print()}time.Sleep(3 * time.Second)// 输出 three three three }// 正确示例func main() {data := []field{{"one"}, {"two"}, {"three"}}for _, v := range data {v := vgo v.print()}time.Sleep(3 * time.Second)// 输出 one two three}// 正确示例func main() {data := []*field{{"one"}, {"two"}, {"three"}}for _, v := range data { // 此时迭代值 v 是三个元素值的地址,每次 v 指向的值不同go v.print()}time.Sleep(3 * time.Second)// 输出 one two three}
// 在 defer 函数中参数会提前求值func main() {var i = 1defer fmt.Println("result: ", func() int { return i * 2 }())i++}
// www.topgoer.com go语言中文文档// 命令行参数指定目录名// 遍历读取目录下的文件func main() {if len(os.Args) != 2 {os.Exit(1)}dir := os.Args[1]start, err := os.Stat(dir)if err != nil || !start.IsDir() {os.Exit(2)}var targets []stringfilepath.Walk(dir, func(fPath string, fInfo os.FileInfo, err error) error {if err != nil {return err}if !fInfo.Mode().IsRegular() {return nil}targets = append(targets, fPath)return nil})for _, target := range targets {f, err := os.Open(target)if err != nil {fmt.Println("bad target:", target, "error:", err) //error:too many open filesbreak}defer f.Close() // 在每次 for 语句块结束时,不会关闭文件资源// 使用 f 资源}}
#!/bin/bashfor n in {1..10000}; doecho content > "file${n}.txt"done
// 目录遍历正常func main() {// ...for _, target := range targets {func() {f, err := os.Open(target)if err != nil {fmt.Println("bad target:", target, "error:", err)return // 在匿名函数内使用 return 代替 break 即可}defer f.Close() // 匿名函数执行结束,调用关闭文件资源// 使用 f 资源}()}}
func First(query string, replicas []Search) Result {c := make(chan Result)replicaSearch := func(i int) { c <- replicas[i](query) }for i := range replicas {go replicaSearch(i)}return <-c}
使用带缓冲的 channel,确保能接收全部 goroutine 的返回结果:
func First(query string, replicas ...Search) Result {c := make(chan Result,len(replicas))searchReplica := func(i int) { c <- replicas[i](query) }for i := range replicas {go searchReplica(i)}return <-c}
使用 select 语句,配合能保存一个缓冲值的 channel default 语句: default 的缓冲 channel 保证了即使结果 channel 收不到数据,也不会阻塞 goroutine
func First(query string, replicas ...Search) Result {c := make(chan Result,1)searchReplica := func(i int) {select {case c <- replicas[i](query):default:}}for i := range replicas {go searchReplica(i)}return <-c}
使用特殊的废弃(cancellation) channel 来中断剩余 goroutine 的执行:
1.1.3. 高级篇:52-58
type data struct {name string}type printer interface {print()}func (p *data) print() {fmt.Println("name: ", p.name)}func main() {d1 := data{"one"}d1.print() // d1 变量可寻址,可直接调用指针 receiver 的方法var in printer = data{"two"}in.print() // 类型不匹配m := map[string]data{"x": data{"three"},}m["x"].print() // m["x"] 是不可寻址的 // 变动频繁}
// 无法直接更新 struct 的字段值type data struct {name string}func main() {m := map[string]data{"x": {"Tom"},}m["x"].name = "Jerry"}
type data struct {name string}func main() {s := []data{{"Tom"}}s[0].name = "Jerry"fmt.Println(s) // [{Jerry}]}
使用局部变量
// 提取整个 struct 到局部变量中,修改字段值后再整个赋值type data struct {name string}func main() {m := map[string]data{"x": {"Tom"},}r := m["x"]r.name = "Jerry"m["x"] = rfmt.Println(m) // map[x:{Jerry}]}
使用指向元素的 map 指针
func main() {m := map[string]*data{"x": {"Tom"},}m["x"].name = "Jerry" // 直接修改 m["x"] 中的字段fmt.Println(m["x"]) // &{Jerry}}
func main() {m := map[string]*data{"x": {"Tom"},}m["z"].name = "what???" fmt.Println(m["x"])}
func main() {var data *bytevar in interface{}fmt.Println(data, data == nil) // <nil> truefmt.Println(in, in == nil) // <nil> truein = datafmt.Println(in, in == nil) // <nil> false // data 值为 nil,但 in 值不为 nil}
// 错误示例func main() {doIt := func(arg int) interface{} {var result *struct{} = nilif arg > 0 {result = &struct{}{}}return result}if res := doIt(-1); res != nil {fmt.Println("Good result: ", res) // Good result: <nil>fmt.Printf("%T\n", res) // *struct {} // res 不是 nil,它的值为 nilfmt.Printf("%v\n", res) // <nil>}}// 正确示例func main() {doIt := func(arg int) interface{} {var result *struct{} = nilif arg > 0 {result = &struct{}{}} else {return nil // 明确指明返回 nil}return result}if res := doIt(-1); res != nil {fmt.Println("Good result: ", res)} else {fmt.Println("Bad result: ", res) // Bad result: <nil>}}
func main() {fmt.Println(runtime.GOMAXPROCS(-1)) // 4fmt.Println(runtime.NumCPU()) // 4runtime.GOMAXPROCS(20)fmt.Println(runtime.GOMAXPROCS(-1)) // 20runtime.GOMAXPROCS(300)fmt.Println(runtime.GOMAXPROCS(-1)) // Go 1.9.2 // 300}
var _ = runtime.GOMAXPROCS(3)var a, b intfunc u1() {a = 1b = 2}func u2() {a = 3b = 4}func p() {println(a)println(b)}func main() {go u1() // 多个 goroutine 的执行顺序不定go u2()go p()time.Sleep(1 * time.Second)}
func main() {done := falsego func() {done = true}()for !done {}println("done !")}
func main() {done := falsego func() {done = true}()for !done {println("not done !") // 并不内联执行}println("done !")}
func main() {done := falsego func() {done = true}()for !done {runtime.Gosched()}println("done !")}
发表评论 取消回复