Go项目结构

  • go.mod

  • go.sum

  • *.go

go.mod

是相关Go包的集合,是源代码交换和版本控制的单元

其提供了module, requirereplaceexclude四个命令

1
2
3
4
5
6
7
module hello

go 1.16

replace example.com/greetings => ../greetings

require example.com/greetings v1.1.0

一个典型的go.mod示例

go.sum

是版本控制相关文件,包含了依赖的module的版本及其Hash

*.go

Go的源文件

1
2
3
4
5
6
7
8
9
10
package hello

import "testing"

func TestHello(t *testing.T) {
want := "Hello, world."
if got := Hello(); got != want {
t.Errorf("Hello() = %q, want %q", got, want)
}
}

一个典型的Go程序代码

创建Go Module的典型过程(一点点shell命令)

创建文件夹

1
2
mkdir hello
cd hello

初始化Go Module

1
go mod init example.com/hello

单元测试

1
2
go test
go test -v # More Detail

显示全部依赖的包

1
go list -m all

添加依赖

1
go get example.com/example

清理依赖

1
go mod tidy

语法

注释

支持 C 风格的块注释 /* */ 和 C++ 风格的行注释 //

btw,顶级声明前面的注释都将作为该声明的文档注释

控制结构

if

1
2
3
4
if condition {
/* Code Here */
return result
}

最简单的if结构

for

1
2
3
4
5
6
7
8
// 类似 C 语言中的 for 用法
for init; condition; post { }

// 类似 C 语言中的 while 用法
for condition { }

// 类似 C 语言中的 for(;;) 用法
for { }

三种常用的方式

1
2
3
for key, value := range oldMap {
newMap[key] = value
}

很方便的切片

Switch

1
2
3
4
5
6
7
8
9
10
11
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}

大概是源码里的

switch不会自动向下追溯,所以要使用逗号分隔相同处理的条件,例如:

1
2
3
4
5
6
7
func shouldEscape(c byte) bool {
switch c {
case ' ', '?', '&', '=', '#', '+', '%':
return true
}
return false
}

结构体、方法与接口

结构体与方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Student struct {
name string
age int
}

func (stu *Student) hello(person string) string {
return fmt.Sprintf("hello %s, I am %s", person, stu.name)
}

func main() {
stu := &Student{
name: "Tom",
}
msg := stu.hello("Jack")
fmt.Println(msg) // hello Jack, I am Tom
}

其他零碎内容

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package greetings

import (
"testing"
"regexp"
)

// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
func TestHelloName(t *testing.T) {
name := "Gladys"
want := regexp.MustCompile(`\b`+name+`\b`)
msg, err := Hello("Gladys")
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
}
}

// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
msg, err := Hello("")
if msg != "" || err == nil {
t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
}
}

一个单元测试的样例 测试文件一定要包含test字样

testing.T与testing.M

testing.T 是普通测试包
testing.M函数可以在测试函数执行之前做一些其他操作

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- cmd	//函数主程序
- internal //私有库
- pkg //公开库
- vendor //依赖
- web //静态Web资源
- configs //配置
- init //系统初始化
- scripts //安装、构建、部署等脚本
- build //打包和持续集成
- deployments IaaS, Paas, 系统, 容器编排的部署配置和模板
- test //额外的外部测试软件和测试数据
- docs //用户及设计文档
- examples //应用或者库的示例文件
- tools //项目的支持工具
- third_party //外部辅助工具, forked 代码, 以及其他第三方工具
- githooks
- assets //资源文件
- website //站点配置数据

错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package greetings

import (
"errors"
"fmt"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
// If no name was given, return an error with a message.
if name == "" {
return "", errors.New("empty name")
}

// If a name was received, return a value that embeds the name
// in a greeting message.
message := fmt.Sprintf("Hi, %v. Welcome!", name)
return message, nil
}

并发

协程

在函数或方法前添加 go 关键字能够在新的 Go 协程中调用它。当调用完成后, 该 Go 协程也会安静地退出。(效果有点像 Unix Shell 中的 & 符号,它能让命令在后台运行。)

1
go list.Sort()  // 同时运行 list.Sort ; 不需要等待

参考

https://learnku.com/docs/effective-go/2020

https://golang.org/doc/tutorial