go学习笔记(一)
参考资料:*Build Web Application with Golang*
开发环境
vscode + go1.21.5 +wsl Ubuntu20.04
go项目结构分为GoPath和GoModules两种。
GoPath
在GoPath模式下,所有和go项目相关的文件都在环境变量$GOPATH指向的路径下。在该路径下存在三个子路径:
1 | $GOPATH |
src存放所有的 Go 源代码文件,pkg存放编译后的库文件(.a
文件),bin存放可执行文件。
这样导致的问题就是文件结构不自由并且多个项目的依赖管理困难。
注:$GOROOT和$GOPATH的区别,$GOPATH是go项目相关,$GOROOT则是Go的安装路径。
GoModules
在 go1.11后推出了GoModules 模式,GoModules 模式主要依赖于官方发布了自己的包管理工具,即 mod。GO111MODULE默认为空,此时为auto模式。在auto模式下如果项目下存在 go.mod 文件时,就启用 GoModules 模式。如果手动设置为on则忽略$GOPATH文件夹。
手动设置的命令为
1 | go env -w GO111MODULE=on |
项目结构
在空文件夹下执行go init test/mymath
,会生成一个go.mod文件。该文件用于包管理。随后新建两个go文件main.go和sqrt.go
1 | //main.go |
此时结构如下:
1 | . |
在go项目中main包中的main函数是程序入口。同一路径下的文件从属于同一个包,可以直接相互调用,否则需要引入包。
但是在该例子中,直接运行go run main.go
会报错./main.go:8:39: undefined: Sqrt
。这是由于如果编译的包名是main包,系统不会自动编译引用的同一包的相关文件,此时会报错:xxx变量undefined;xxx函数undefined。
1 | Each go file is in some package, and that package should be a distinct folder in the GOPATH, but main is a special package which doesn't require a main folder. This is one aspect which they left out for standardization! But should you choose to make a main folder then you have to ensure that you run the binary properly. Also one go code can't have more than one main go file. |
可以使用命令go run *.go
。
语法相关
Go 使用 UTF-8 字符集。字符串由双引号 “” 或反引号 `` 表示。但是双引号””不能跨行,反引号``则可以,且不会转义任何字符。
在go中动态数组被称为slice。定义方式和数组一样,不指定具体的长度就可以了。
可变参函数
1
2
3
4
5func myfunc(arg ...int) {
for _, n := range arg {
fmt.Printf("And the number is: %d\n", n)
}
}defer的用法,defer在函数执行完后开始从后往前执行,适用于一些结束的工作
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
27
28
29func ReadWrite() bool {
file.Open("file")
// Do some work
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
//defer改写
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}导包的时候 _ 运算符实际上意味着我们只想导入该包并执行其 init 函数,不确定是否要使用属于该包的函数。
1
2
3
4import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
interface相关
在go中interface有以下作用:
使用同一个interface将不同的struct存在同一个slice中,slice的类型为interface。
fmt的源码
1
2
3type Stringer interface {
String() string
}
因此只要实现了fmt.Stringer,就可以直接通过fmt.Println输出。
1 | package main |
空接口
空接口可以存任意类型的值
1
2
3
4
5
6
7
8
9
10// define a as empty interface
var void interface{}
// vars
i := 5
s := "Hello world"
// a can store value of any type
void = i
void = s