Go编程tips-3

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

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)
}

"switch"

分支默认不是继续下一个分支,默认是'break';
支持条件列表,通过逗号隔开
如果要强制进入下一分支,可以使用"fallthrough"

自增自减

不支持前置,只支持后置,也就是只支持 "i++"的用法

位运算

取反:^ XOR
与: & AND
与非:&^ AND NOT

操作符优先级

&优先于+
<<优先于+
|优先于^

未导出的结构不会被编码

[{ loading ... }]将会得到零值。下面的示例中,成员two没有被编码,所以再反序列化时,就变成空值了。

package main

import (  
    "fmt"
    "encoding/json"
)

type MyData struct {  
    One int
    two string
}

func main() {  
    in := MyData{1,"two"}
    fmt.Printf("%#v\n",in) //prints main.MyData{One:1, two:"two"}

    encoded,_ := json.Marshal(in)
    fmt.Println(string(encoded)) //prints {"One":1}

    var out MyData
    json.Unmarshal(encoded,&out)

    fmt.Printf("%#v\n",out) //prints main.MyData{One:1, two:""}
}

有coroutines的应用的退出

  • 应用是不会等待所有goroutines都完成才退出的,主应用和goroutine之间默认是不交互和感知的。

向关闭的chnnel发送数据会panic

操作没有初始化的"nil"的channel

在一个nil的channel上发送和接收操作会被永久阻塞。这个行为可以在select声明中用于动态开启和关闭case代码块的方法。

package main

import "fmt"
import "time"

func main() {
    inch := make(chan int)
    outch := make(chan int)

    go func() {
        var in <-chan int = inch
        var out chan<- int
        var val int
        for {
            select {
            case out <- val:
                out = nil
                in = inch
            case val = <-in:
                out = outch
                in = nil
            }
        }
    }()

    go func() {
        for r := range outch {
            fmt.Println("result:", r)
        }
    }()

    time.Sleep(0)
    inch <- 1
    inch <- 2
    time.Sleep(3 * time.Second)
}

HTTP响应的关闭

当你使用标准http库发起请求时,你得到一个http的响应变量。如果你不读取响应主体,你依旧需要关闭它。注意对于空的响应你也一定要这么做。对于新的Go开发者而言,这个很容易就会忘掉。
大多数情况下,当你的http响应失败时,resp变量将为nil,而err变量将是non-nil。然而,当你得到一个重定向的错误时,两个变量都将是non-nil。这意味着你最后依然会内存泄露。
通过在http响应错误处理中添加一个关闭non-nil响应主体的的调用来修复这个问题。另一个方法是使用一个defer调用来关闭所有失败和成功的请求的响应主体。

package main

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

func main() {
    resp, err := http.Get("https://api.ipify.org?format=json")
    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(string(body))
    _, err = io.Copy(ioutil.Discard, resp.Body)
}

resp.Body.Close()的原始实现也会读取并丢弃剩余的响应主体数据。这确保了http的链接在keepalive http连接行为开启的情况下,可以被另一个请求复用。最新的http客户端的行为是不同的。现在读取并丢弃剩余的响应数据是你的职责。如果你不这么做,http的连接可能会关闭,而不是被重用。这个小技巧应该会写在Go 1.5的文档中。

关闭HTTP连接

一些HTTP服务器保持会保持一段时间的网络连接(根据HTTP 1.1的说明和服务器端的“keep-alive”配置)。默认情况下,标准http库只在目标HTTP服务器要求关闭时才会关闭网络连接。这意味着你的应用在某些条件下消耗完sockets/file的描述符。
你可以通过设置请求变量中的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
    //or do this:
    //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)))
}

比较Structs, Arrays, Slices, and Maps

只有每个成员本身能用"=="比较时,对象才可以用"=="比较。

DeepEqual()函数

对于不能直接比较的,可以使用DeepEqual函数
package main

import (
    "fmt"
    "reflect"
)

type data struct {
    num    int               //ok
    checks [10]func() bool   //not comparable
    doit   func() bool       //not comparable
    m      map[string]string //not comparable
    bytes  []byte            //not comparable
}

func main() {
    v1 := data{}
    v2 := data{}
    fmt.Println("v1 == v2:", reflect.DeepEqual(v1, v2)) //prints: v1 == v2: true

    m1 := map[string]string{"one": "a", "two": "b"}
    m2 := map[string]string{"two": "b", "one": "a"}
    fmt.Println("m1 == m2:", reflect.DeepEqual(m1, m2)) //prints: m1 == m2: true

    s1 := []int{1, 2, 3}
    s2 := []int{1, 2, 3}
    fmt.Println("s1 == s2:", reflect.DeepEqual(s1, s2)) //prints: s1 == s2: true
}

DeepEqual()不会认为空的slice与“nil”的slice相等。这个行为与你使用bytes.Equal()函数的行为不同。bytes.Equal()认为“nil”和空的slice是相等的。

如果你的byte slice(或者字符串)中包含文字数据,而当你要不区分大小写形式的值时(在使用==,bytes.Equal(),或者bytes.Compare()),你可能会尝试使用“bytes”和“string”包中的ToUpper()或者ToLower()函数。对于英语文本,这么做是没问题的,但对于许多其他的语言来说就不行了。这时应该使用strings.EqualFold()和bytes.EqualFold()。

如果你的byte slice中包含需要验证用户数据的隐私信息(比如,加密哈希、tokens等),不要使用reflect.DeepEqual()、bytes.Equal(),或者bytes.Compare(),因为这些函数将会让你的应用易于被定时攻击。为了避免泄露时间信息,使用'crypto/subtle'包中的函数(即,subtle.ConstantTimeCompare())。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注