GO踩坑集锦

GO踩坑集锦

项目环境

首先我们得把自己的项目目录加入环境变量里,不然它找不到包啊。

GOPATH=$HOME/go:your/project/path

目录结构

  • 每个包都得放到一个独立文件夹里!
  • main包得放到项目root目录下,不然编译过不了。

开发工具栈

  • web框架
  • 开发热重启
  • vscode
    go test设置-v不隐藏日志,-count=1不缓存测试结果。

工具库

strconv

  1. strconv.Itoa(n)
    int型数字n转为十进制字符串
    反向转换strconv.Atoi(s)

  2. strconv.FormatInt(n, 10)
    int64数字n转为十进制字符串
    反向转换strconv.ParseInt(s, 10, 64)

strings

  1. stringReader
r := strings.NewReader("abcde")

io/ioutil

  1. Reader[]byte
r := strings.NewReader("abc") b, err := ioutil.ReadAll(r)

encoding/base64

  1. []bytebase64
fileByte, _ := ioutil.ReadAll(file) fileBase64 := base64.StdEncoding.EncodeToString(fileByte)

GO Tools

godoc

安装

go get -v golang.org/x/tools/cmd/godoc

go-swagger

install

当前项目下生成swagger.json

$ swagger generate spec -o ./swagger.json

swagger.json以web页面形式展示

swagger serve -p=10100 -F=swagger ./swagger.json

GO 类型及转换

整型

整型分为以下两个大类: 按长度分为:int8、int16、int32、int64 对应的无符号整型:uint8、uint16、uint32、uint64

浮点型

Go语言支持两种浮点型数:float32和float64。

GO 基础

make 和 new 的区别

new的函数声明:func(Type) *Type

The value returned is a pointer to a newly allocated zero value of that type.

new对该类型分配一个内存空间并进行了零值初始化,并返回了其指针。

var a *int // (*int)(nil) a = new(int) // (*int)(0xc0000a41b0)

对于引用类型,只有在值的引用指针不为nil时才能对其进行赋值操作。

make的函数声明:func(t Type, size ...IntegerType) Type
new不一样的地方在于,make只能初始化slice,map,channel类型的值,make不仅初始化了内存地址,还为其分配了内存空间,使其初始值不为nil。这样,就可以对其进行赋值操作。

var ap = new([]int) // &[]int(nil) var ap1 = new([1]int) // &[1]int{0} var a = make([]int, 0, 0) // []int{} var mp = new(map[string]string) // map[string]string(nil) var m = make(map[string]string) // map[string]string{}

这一点对于map来说十分重要,因为没有指定大小,new操作并没有为其分配内存空间,尝试为其赋值会出错。但是make会为其分配一个很小的空间。

var mp = *new(map[string]string) mp["a"] = "1" // panic: assignment to entry in nil map

string的实现

string的底层是[]byte它的底层结构为

type StringHeader struct { Data uintptr Len int }

字符串的内容就是StringHeader.Data所指向的[]byte

fmt格式

Printf用于格式化字符串,其中拥有多种占位符,每个占位符都有自己的格式化结果。

fmt.Printf("%s", "text")
占位符 描述 例子
%v 默认格式化 fmt.Printf("%v%v\n", 1, "xxx") -> “1xxx”
%s 转换字符串 fmt.Printf("%s", "text") -> “text”
%t 转换布尔值 fmt.Printf("%t", true) -> “true”
%b,%d,%o,%x 转换进制 fmt.Printf("%b", 888) -> “1101111000”
%p 转换指针 fmt.Printf("%p", new(int)) -> “0xc00011c198”
%f 转换浮点数 fmt.Printf("%.4f", math.Pi) -> “3.1415”

字符串字符长度

由于golang默认使用utf-8编码格式,所以如果需要计算字符数而不是字节数,要这么用

import "unicode/utf8" len("你好") // 6,中文占3个字节 utf8.RuneCountInString("你好") // 2

遍历字符串

for _, v := range "你好" {} // 相当于 for _, v := range []rune("你好") {}
for _, v := range "你" { fmt.Println(v) // 20320 fmt.Printf("%c", v) // 你 fmt.Println(string(v)) // 你 }

参数解构

func foo(args ...int) { bar(args...) }

通过...typeslice...,可以将多个参数变为切片,以及将切片变为多个参数传递。

GO 最佳实践

使用buffer拼接字符串

由于字符串是不变的,使用a+=b形式拼接字符串会消耗过多内存,所以建议使用byte buffer的形式

var b bytes.Buffer ... for condition { b.WriteString(str) // 将字符串str写入缓存buffer } return b.String()

指针和接口类型

永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。

,ok模式

在Golang中,基于多返回值的特性,在返回正确结果时,我们也可以定义返回的错误异常。所以就有了,ok的模式。

if val, ok := dosomething(); ok {}

这种模式常被用于:

1. 检查函数错误

if val, err := func(); err != nil { // 处理错误 } // 处理val

2. 检查键是否存在

if val, exist := somemap["key"]; exist {}

3. 类型断言

if val, ok := value.(T); ok {}

4. 检查通道关闭

for { if val, open := <-ch; !open { break } }

复制类型切片到空接口切片

如果你想这样操作:

var a []sometype var b []interface{} = a

这样是不行的,因为他们的内存布局不一样,需要进行循环转换。反过来也一样。

GO 测试

GO 自带了单元测试和性能测试功能

go test ./...

以上命令会将目录下的所有测试文件(以_test.go结尾)都跑一遍。

-v 选项

显示打印日志

-run regexp

只执行特定的测试函数,通过一个正则表达式匹配需要执行的测试
比如:

go test -run TestFoo ./...

只会执行函数名以TestFoo开头的测试函数

取消缓存

使用-count=1来取消测试缓存

跳过测试

func TestFoo(t *testing.T) { t.Skip("reason") // ... }
上一篇 单页应用实现原理
下一篇 Golang+Vue项目Docker化