Golang 开发学习路线:高阶

896 阅读2分钟

前面把 Go 语言中阶的学习路线整理出来了:Golang 开发学习路线:中阶,今天最后把 Go 语言(高阶)的学习路线也整理完了。

学习路线脑图如下: https://gitmind.cn/app/doc/8bc3b64fb1fe8787e6ab7f91c431a89

另外今天也给大家聊聊关于使用 Go 的一些规范指南,或者说平时写代码需要做的一些 CheckLIst。

【必须】切片长度校验

  • 在对 slice 进行操作时,必须判断长度是否合法,防止程序 panic
package main

import "fmt"

// bad: 未判断data的长度,可导致 index out of range
func badDecode(data []byte) bool {
	if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'E' && data[5] == 'R' {
		fmt.Println("Bad")
		return true
	}
	return false
}

// bad: slice bounds out of range
func foo() {
	slice := []int{0, 1, 2, 3, 4, 5, 6}
	fmt.Println(slice[:10])
}

// good: 使用data前应判断长度是否合法
func goodDecode(data []byte) bool {
	if len(data) == 6 {
		if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'E' && data[5] == 'R' {
			fmt.Println("Good")
			return true
		}
	}
	return false
}

func main() {
	data := []byte{'A', 'B', 'C', 'D'}
	fmt.Println(goodDecode(data))
	fmt.Println(badDecode(data))
}

【必须】nil 指针判断

  • 进行指针操作时,必须判断该指针是否为 nil,防止程序 panic,尤其在进行结构体  Unmarshal 时
package main

import (
	"fmt"
	"io"
)

type Packet struct {
	PackeyType uint8
	PackeyVersion uint8
	Data *Data
}
type Data struct {
	Stat uint8
	Len uint8
	Buf [8]byte
}
func (p *Packet) UnmarshalBinary(b []byte) error {
	if len(b) < 2 {
		return io.EOF
	}

	p.PackeyType = b[0]
	p.PackeyVersion = b[1]
	// 若长度等于2,那么不会new Data
	if len(b) > 2 {
		p.Data = new(Data)
		// Unmarshal(b[i:], p.Data)
	}
	return nil
}

// bad: 未判断指针是否为nil
func main() {
	packet := new(Packet)
	data := make([]byte, 2)
	if err := packet.UnmarshalBinary(data); err != nil {
		fmt.Println("Failed to unmarshal packet")
		return
	}
	fmt.Printf("Stat: %v\n", packet.Data.Stat)
}

【必须】整数安全

在进行数字运算操作时,需要做好长度限制,防止外部输入运算导致异常:

  • 确保无符号整数运算时不会反转
  • 确保有符号整数运算时不会出现溢出
  • 确保整型转换时不会出现截断错误
  • 确保整型转换时不会出现符号错误

以下场景必须严格进行长度限制:

  • 作为数组索引
  • 作为对象的长度或者大小
  • 作为数组的边界(如作为循环计数器)
package main

import "fmt"

// bad: 未限制长度,导致整数溢出
func overflowBad(numControlByUser int32) {
	var numInt int32 = 0
	numInt = numControlByUser + 1
	//对长度限制不当,导致整数溢出
	fmt.Printf("%d\n", numInt)
	//使用numInt,可能导致其他错误
}

// good:
func overflowGood(numControlByUser int32) {
	var numInt int32 = 0
	numInt = numControlByUser + 1
	if numInt < 0 {
	fmt.Println("integer overflow")
	return;
	}
	fmt.Println("integer ok")
	}

func main() {
	overflowBad(2147483647)
	overflowGood(2147483647)
}

【必须】make 分配长度验证

  • 在进行 make 分配内存时,需要对外部可控的长度进行校验,防止程序 panic。
package main

import "errors"

// bad
func parseBad(lenControlByUser int, data[] byte) {
	size := lenControlByUser
	//对外部传入的size,进行长度判断以免导致panic
	buffer := make([]byte, size)
	copy(buffer, data)
}

// good
func parseGood(lenControlByUser int, data[] byte) ([]byte, error){
	size := lenControlByUser
	//限制外部可控的长度大小范围
	if size > 64*1024*1024 {
		return nil, errors.New("value too large")
	}
	buffer := make([]byte, size)
	copy(buffer, data)
	return buffer, nil
}

总结

主要梳理了 Go 语言高阶学习路线,以及整理了一些  Go 的使用规范指南,后续会继续补充这一块的内容。

代码示例已更新到 github-gochecklist

推荐阅读: Golang 开发学习路线:中阶