Go项目结构

  • go.mod

  • go.sum

  • *.go

go.mod

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

其提供了module, requirereplaceexclude四个命令

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的源文件

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命令)

创建文件夹

mkdir hello
cd hello

初始化Go Module

go mod init example.com/hello

单元测试

go test
go test -v # More Detail

显示全部依赖的包

go list -m all

添加依赖

go get example.com/example

清理依赖

go mod tidy

语法

注释

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

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

控制结构

if

if condition {
	/* Code Here */
    return result
}

最简单的if结构

for

// 类似 C 语言中的 for 用法
for init; condition; post { }

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

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

三种常用的方式

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

很方便的切片

Switch

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不会自动向下追溯,所以要使用逗号分隔相同处理的条件,例如:

func shouldEscape(c byte) bool {
    switch c {
    case ' ', '?', '&', '=', '#', '+', '%':
        return true
    }
    return false
}

结构体、方法与接口

结构体与方法

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
}

其他零碎内容

单元测试

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函数可以在测试函数执行之前做一些其他操作

项目结构

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

错误处理

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 中的 & 符号,它能让命令在后台运行。)

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

参考

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

https://golang.org/doc/tutorial