Go 语言小细节(二)


数组用于函数传参时是值复制
方法或函数调用时,传入参数都是值复制,跟赋值一致,除非是 map、slice、channel、指针类型这些特殊类型是引用传递。

x := [3]int{1,2,3}

数组在函数中传参是值复制

func(arr [3]int) {
    arr[0] = 7
    fmt.Println(arr)
    // [7 2 3]
}(x)
fmt.Println(x)
// [1 2 3]

使用数组指针实现引用传参

func(arr *[3]int) {
    (*arr)[0] = 7
    fmt.Println(arr)
    // &[7 2 3]
}(&x)
fmt.Println(x)
// [7 2 3]

字符串与 []byte 之间的转换是复制,有内存损耗,可以用 map[string][]byte 建立字符串与 []byte 之间映射,也可
range 来避免内存分配来提高性能

for i,v := range []byte(str) {
}

向一个 nil 值(未用 make 分配空间)的 channel 发送或读取数据,会导致永远阻塞。

package main

import (  
    "fmt"
    "time"
)

func main() {  
    var ch chan int
    for i := 0; i < 3; i++ {
        go func(idx int) {
            ch <- (idx + 1) * 2
        }(i)
    }
    fmt.Println("result:",<-ch)
    time.Sleep(2 * time.Second)
}

log 包中的 log.Fatal 和 log.Panic 不仅仅记录日志,还会中止程序。它不同于 Logging 库。

关闭 http 连接
可使用 req.Close=true,表示在 http 请求完成时关闭连接
添加 Connection: close 的连接请求头。http 服务端也会发送 Connection: close 的响应头,http 库处理响应时会关闭连接。

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    req, err := http.NewRequest("GET","http://golang.org",nil)
    if err != nil {
        fmt.Println(err)
        return
    }

    req.Close = true
    //req.Header.Add("Connection", "close")

    resp, err := http.DefaultClient.Do(req)
    if resp != nil {
        defer resp.Body.Close()
    }
    if err != nil {
        fmt.Println(err)
        return
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(len(string(body)))
}

全局关闭 http 连接重用
对于相同 http server 发送多个请求时,适合保持网络连接。但对于短时间内向多个 http 服务器发送一个或两个请求时,最好在每次接收到服务端响应后关闭网络链接

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    tr := &http.Transport{DisableKeepAlives: true}
    client := &http.Client{Transport: tr}

    resp, err := client.Get("http://golang.org")
    if resp != nil {
        defer resp.Body.Close()
    }

    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(resp.StatusCode)

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(len(string(body)))
}

在 slice、array、map 的 for range 子句中修改和引用数据项
使用 range 获取的数据项是从集合元素的复制过来的,并非引用原始数据,但使用索引能访问原始数据。

data := []int{1,2,3}
for _,v := range data {
    v *= 10
}

data2 := []int{1,2,3}
for i,v := range data2 {
    data2[i] *= 10
}

元素是指针类型就不一样了

data3 := []*struct{num int} {{1}, {2}, {3}}
for _,v := range data {
    v.num *= 10
}
fmt.Println("data:", data)
// data: [1 2 3]
fmt.Println("data:", data2)
// data: [10 20 30]
fmt.Println(data3[0],data3[1],data3[2])
// &{10} &{20} &{30}
分享:

评论