Go
引言
十分入门的一本书?精确来说是一篇 GO 的入门文章,用于学学英语和简略理解 GO 还不错,并且词汇和语法都比拟简洁易懂。
话不多说,上面就来看看这篇文章的翻译。
原文
https://www.freecodecamp.org/news/go-beginners-handbook/
1. 介绍 GO
它是由谷歌工程师创立的,设计这门语言的次要指标是:
- 进步我的项目的编译和运行速度
- 入门门槛足够低,然而也要防止过于简略和低级。
- 具备可移植(编译后的 Go 程序是二进制文件,不须要其余文件就能运行,而且是跨平台的,因而能够很容易地散发)
- 枯燥、稳固、可预测,犯错的机会少。
- 能充分发挥多线程劣势
2. 如何开始?
在深刻理解该语言的具体内容之前,您应该理解以下几件事。
首先,https://go.dev 是该语言的主页。
上面是首选的入门资源:
- 从 https://go.dev/doc/install 下载 Go 二进制文件(go 命令和其余相干工具)。
- 参考 Go 官网文档:https://go.dev/doc/
- 查看所有 Go 软件包:https://pkg.go.dev/
- 拜访 Go Playground:https://go.dev/play/
- …
3. 装置 GO
装置 GO 的教程网上一抓一大把,集体应用的是 Window 环境,Windows 环境下只须要装置 exe 应用程序即可,简略无脑就不演示了。
4. 开发 IDE 装置和配置
NOTE: you might have to open a new terminal before you can run the program, as the installer added the Go binaries folder to the path.
留神:因为安装程序在门路中增加了 Go 二进制文件夹,运行程序前可能须要关上一个新的终端。
在应用 IDE 之前,请先确保本人的环境变量能够失常应用 GO。
本书应用的是 VS Code,具体能够浏览此网站理解:Go with Visual Studio Code。
[[【Go】Go in Visual Studio Code]]
VS Code 装置之后,须要装置 GO 相干的扩大:marketplace.visualstudio.com/items?itemName=golang.go
5. 你好,世界
- 创立一个新的文件夹,比方上面的 hello。
- 创立一个
hello.go
的文件,并且在文件内写入上面的内容:
package main
import "fmt"
func main() {fmt.Println("Hello, World!")
}
- 在文件所在门路,运行
go run hello.go
,如果后果如下阐明运行胜利:
xander@LAPTOP-47J243NL MINGW64 /e/adongstack/go/hello
$ go run hello.go
Hello, World!
上面来解释下下面的入门程序:
- 每个 .go 文件首先申明它是哪个软件包的一部分
- 一个软件包能够由多个文件组成,也能够只由一个文件组成。一个程序能够蕴含多个软件包。
main
函数是程序的入口点,也是可执行程序的标识。- 咱们应用 import 关键字来导入程序包。
fmt is a built-in package provided by Go that provides input/ output utility functions.
fmt 是 Go 提供的一个内置包,提供输出 / 输入实用功能。
GO 官网领有宏大的规范库,从网络连接到数学、明码、图像处理、文件系统拜访等,咱们都能够应用它。
Standard library – Go Packages
比方,无关fmt
这个包的文档介绍能够查看这个地址理解:fmt package – fmt – Go Packages,依据文档,该函数性能是 “ 依据格局说明符进行格式化,并写入规范输入 ”。
咱们应用 “ 点 “ 语法 fmt.Println()
来指定该函数由该程序包提供。
当代码执行完主函数后,就没有其余事件可做了,执行完结。
在 main 函数中,咱们定义了 fmt.Println("Hello, World!")
这样一串代码。
6. 编译并运行 Go 程序
本局部接着上一章节的入门程序介绍,解释如何编译并且运行 go 程序
go run hello.go
go run 工具首先编译,而后运行指定的程序。
咱们能够应用 go build
创立二进制文件:
go build hello.go
个人电脑上执行的后果如下:
PS E:\adongstack\go\hello> go build .\hello.go
PS E:\adongstack\go\hello> dir
目录: E:\adongstack\go\hello
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2023/9/19 13:33 1897472 hello.exe
-a---- 2023/9/19 13:32 84 hello.go
Go 是可移植的,因为实质编译之后实质就是二进制文件,这样每个人都能够按原样运行程序,并且二进制文件曾经打包供执行。
程序将在咱们构建它的雷同架构上运行。
咱们能够应用 GOOS
和 GOARCH
环境变量为不同的架构创立不同的二进制文件:
GOOS = windows GOARCH = amd64 go build hello.go
这将为 64 位 Windows 机器创立一个 hello.exe 可执行文件:
64 位 macOS(英特尔或苹果)的设置为
GOOS = darwin GOARCH = amd64
Linux 为 :
GOOS = linux GOARCH = amd64
这是 Go 的最佳性能之一。
7. 工作空间
Go 有一个特别之处就是咱们所说的工作空间。
工作区是 Go 的 “ 大本营 ”。
默认状况下,Go 会抉择 $ HOME/ go
门路,因而你会在主目录中看到一个 go 文件夹。
例如,当我在 VS Code 中加载 hello.go 文件时,它提醒我装置 gopls 命令、Delve 调试器 (dlv) 和 staticcheck linter。
它们已主动装置在 $ HOME/ go
下:
当你应用 go install 装置软件包时,它们将被存储在这里。
这就是咱们所说的 GOPATH。
你能够更改 GOPATH 环境变量来扭转 Go 安装包的地位。
当你同时在不同的我的项目中工作,并心愿隔离你应用的库时,这一点十分有用。
8. 深刻语言
既然咱们曾经有了初步的概念,并运行了第一个 Hello, World! 程序,那么咱们就能够深入研究这门语言了。
这种语言没有语义上重要的空白。
就像 C、C + +、Rust、Java
和 JavaScript。与 Python
不同的是,Python
的空白是有意义的,它被用来创立块而不是大括号。
Go 非常重视 缩进和视觉程序。装置 Go 时,咱们还能够应用 gofmt 命令行工具来格式化 Go 程序。
VS Code 在引擎盖下应用该工具来格式化 Go 源文件。
这是一个十分有意思的个性,语言创建者定义了规定,每个人都会应用这些规定。
这非常适合大型团队的我的项目。
本书倡议在 VS Code 当中设置“Format on Save”以及“Format on Paste”:
Go 中的正文应用常见的 C / C + + / JavaScript / Java 语法:
9. 变量
在 GO 外面能够是用 var
关键字定义变量:
var age = 20
var
关键字能够定义包级别变量:
package main
import "fmt"
var age = 20
func main() {fmt.Println("Hello, World!")
}
或者定义在函数外部:
package main
import "fmt"
func main() {
var age = 20
fmt.Println("Hello, World!")
}
在软件包级别定义的变量在组成软件包的所有文件中都是可见的。
一个软件包能够由多个文件组成,只需创立另一个文件,并在顶部应用雷同的软件包名称即可。
在函数外部定义的变量,一个变量只能在函数中可见。
这使得 Go 判断变量 age 的类型是 int。
咱们稍后会看到更多对于类型的内容,但你应该晓得有许多不同的类型,首先是 int、string 和 bool。
咱们也能够申明一个没有现存值的变量,但在这种状况下,咱们必须像这样设置类型:
var age int
var name string
var done bool
当您晓得变量值时,通常应用带有 :=
操作符的短变量申明:
age := 10
name := "Roger"
留神变量名称是辨别大小写的,如果名称较长,通常应用 驼峰字母大写,因而咱们应用 carName
来示意汽车的名称。
您能够应用赋值运算符 =
为变量赋值。
var age int
age = 10
age = 11
如果一个变量确定不会扭转,能够应用 const
关键字定义:
const age = 10
你能够在一行定义多个变量:
var age, name = 10, "Roger"
// or
age, name := 10, "Roger"
如果定义了变量然而没有应用,在 VS Code 中会有相干提醒。
这些错误信息理论是编译
如果您申明一个变量,但没有将其初始化为一个值,那么它会主动调配一个值,这个值取决于变量的类型,例如整数为 0,字符串为空字符串。
10. 根本类型
Go 的根本类型有
- 整数(int、int8、int16、int32、rune、int64、uint、uintptr、uint8、uint16、uint64)
- 浮点数(float32、float64),用于示意小数
- 复数类型(complex64、complex128),用于数学运算
- 字节(byte),示意单个 ASCII 字符
- 字符串(string),一组字节
- 布尔类型(bool),示意真或假
咱们有很多不同的类型来示意 interger,大部分工夫你都会应用 int,你可能会抉择一个更业余的类型来进行优化(刚开始学习时不须要思考这些)。
int
类型针对 32 位机器和 64 位的机器做了类型适配,
uint
是一个无符号的 int
,如果晓得数值不会是正数,就能够用它来加倍存储数值。
11. 字符串
Strings 在 Go 语言中是一串字节数组。
咱们能够应用上面的语法定义一串字符串:
var name = "test"
值得注意的是,与其余语言不同,字符串的定义只能应用双引号,而不能应用单引号(比方 JS)。
能够应用len
函数获取字符串长度
len(name) // 4
您能够应用方括号拜访单个字符,同时传递您要获取的字符的索引:
name[0] //"t" (indexes start at 0)
name[1] //"e"
能够应用上面的语法像 Python 语言一样对于字符串进行“切片”:
name[0: 2] //"te"
name[: 2] //"te"
name[2:] //"st"
应用上面的语法能够拷贝一个字符串:
var newstring = name[:]
您能够将一个字符串赋值给一个新变量:
var first = "test"
var second = first
Strings 是不可变变量,意味着你不能更新一个 Strings,即便应用赋值运算符为第一项赋值,第二项的值依然是 “test”:
var first = "test"
var second = first
first = "another test"
first //"another test"
second //"test"
字符串是 援用类型 ,这意味着如果将字符串传递给函数,复制的将是 字符串的援用,而不是其值。
但因为字符串是不可变的,在这种状况下,与传递 int 等类型的字符串在实际操作中并无太大区别。
能够应用 + 运算符连贯两个字符串:
var first = "first"
var second = "second"
var word = first + "" + second //" first second"
Go 在字符串包中提供了多个字符串实用程序。
咱们曾经在 “Hello, World!” 示例中看到了如何导入包,上面介绍如何导入字符串:
以下是导入字符串的办法:
package main
import ("strings")
12. 数组
数组是由繁多类型的我的项目组成的序列。
咱们这样定义一个数组:
var myArray [3] string // 蕴含 3 个字符串的数组
你能够应用以下值初始化数组:
var myArray = [3] string{"First", "Second", "Third"}
在这种状况下,你也能够让 Go 来帮你计数:
var myArray = [...] string{"First", "Second", "Third"}
留神,数组只能蕴含雷同类型的值。
数组不能调整大小,必须在 Go 中明确定义数组的长度。
数组不能调整大小,必须在 Go 中明确定义数组的长度。
这是数组类型的一部分。此外,也不能应用变量来设置数组的长度。
因为这种限度,在 Go 中很少间接应用数组,而是应用 Slice
(稍后会具体介绍)。
留神 Slice 底层也是数组实现的,所以理解数组是根底。
数组能够通过方括号加下标值获取数组特定地位,而后针对特定地位设置新值。
myArray[0] // indexes start at 0
myArray[1]
数组也能够应用len()
函数获取数组长度:
数组是 值类型。这意味着能够复制数组:
anotherArray := myArray
将数组传递给函数,或从函数中返回数组,都会 创立原始数组的正本。
这与其余编程语言不同。
让咱们举一个简略的例子,在复制一个数组项后,给它赋一个新值。
请看,拷贝并没有扭转:
var myArray = [3] string{"First", "Second", "Third"}
myArrayCopy := myArray
myArray[2] = "Another"
myArray[2] //"Another"
myArrayCopy[2] //"Third"
请记住,你只能在数组中增加繁多类型的项,因而设置 myArray[2] = 2
会引发谬误。
底层元素在内存中间断存储。
低级元素继续存储在内存中。
13. 分片
分片是一种相似于数组的数据结构,但它的大小能够扭转。
切片应用数组,是建设在数组之上的一种形象构造,它使切片更灵便、更有用(将数组视为低级构造)。
如果你晓得须要对切片执行操作,你能够要求它领有比最后须要的更大容量,这样当你须要更多空间时,空间就会随时可用(而不是找到切片并将其挪动到一个有更多空间的新内存地位,而后通过垃圾回收解决旧地位)。
咱们能够为 make() 增加第三个参数来指定容量:
newSlice := make([] string, 0, 10) // an empty slice with capacity 10
多个切片能够应用同一个数组作为底层数组:
myArray := [3] string{"First", "Second", "Third"} mySlice = myArray[:]
与字符串一样,应用该语法能够获取片段的一部分:
mySlice := [] string{"First", "Second", "Third"} newSlice := mySlice[: 2] // get the first 2 items newSlice2 := mySlice[2:] // ignore the first 2 items newSlice3 := mySlice[1: 3] // new slice with items in position 1-2
14.Maps
map
是 go 语言的罕用数据类型。
agesMap := make(map[ string] int)
您无需设置地图可包容的物品数量。您能够通过这种形式向 map 增加新内容:
agesMap["flavio"] = 39
您也能够应用以下语法间接用值初始化映射:
agesMap := map[string] int{"flavio": 39}
通过上面的语法能够获取 map 的 key 的值:
age := agesMap["flavio"]
应用delete()
函数能够删除 Map 中对应的 Key。
delete(agesMap, "flavio")
15. 循环
Go 的循环语句关键字也是for
:
for i := 0; i < 10; i + + {fmt.Println( i)
}
咱们首先初始化一个循环变量,而后设置每次迭代时要查看的条件,以决定循环是否应该完结,最初在每次迭代完结时执行 post 语句,在本例中,该语句会递增 i。
i++ 使 i 变量递增。
< 操作符用于将 i 与数字 10 进行比拟,并返回 true 或 false,以决定是否执行循环体。
与 C 或 JavaScript 等其余语言不同,咱们不须要在该代码块四周应用括号。
此外还须要留神,Go 没有 while
这样的循环语法,如果想要实现相似的性能,能够应用 for
进行模仿。
i := 0 for i < 10 {fmt.Println( i) i + +
}
此外能够通过 break
语句跳出循环。
for {fmt.Println( i)
if i < 10 {break}
i ++
}
我在循环体中应用了 if
语句,但咱们还没有看到条件!咱们下一步再做。
当初我要介绍的是范畴。
咱们能够应用 for 语法遍历数组:
numbers := [] int{ 1, 2, 3}
for i, num := range numbers {fmt.Printf("% d: %d\ n", i, num)
}
// 0: 1
// 1: 2
// 2: 3
注:我应用了 fmt.Printf(),它容许咱们应用示意十进制整数的 %d 和示意增加行结束符的 \n 来向终端打印任何值。
当不须要应用 index
时,应用这种语法很常见:
for _, num := range numbers {//...}
应用示意 “ 疏忽这个 “ 的非凡 _
字符,以防止 Go 编译器呈现 “ 你没有应用 i 变量!” 的谬误。
16. 条件式
条件语句的语法和其余语言相似:
if age < 12 {// child} else if age < 18 {// teen} else {// adult}
和其余语言一样,在 if
或者 else
代码块中定义变量,变量的可见范畴等同于代码块的范畴。
如果要应用多个不同的 if
语句来查看一个条件,最好应用 switch
:
switch age
{case 0: fmt.Println("Zero years old")
case 1: fmt.Println("One year old")
case 2: fmt.Println("Two years old")
case 3: fmt.Println("Three years old")
case 4: fmt.Println("Four years old")
default: fmt.Println(i + "years old")
}
与 C、JavaScript 和其余语言相比,您不须要在每种状况后都有一个分隔符。
17. 操作符
到目前为止,咱们在代码示例中应用了一些运算符,如 =、:= 和 <。
咱们应用赋值运算符 = 和 := 来申明和初始化变量:
var a = 1
b := 1
咱们有比拟运算符 == 和 !=,它们承受 2 个参数并返回布尔值。
var num = 1
num == 1 // true
num != 1 // false
当然还有上面的内容:
var num = 1
num > 1 // false
num >= 1 // true
num < 1 // false
num <= 1 // true
咱们有二进制(须要两个参数)算术运算符,如 + - * / %
。
1 + 1 // 2
1 - 1 // 0
1 * 2 // 2
2 / 2 // 1
2 % 2 // 0
+
运算符能够连贯字符串:
"a" + "b" //"ab"
咱们有一元运算符 ++
和 --
来递增或递加数字:
var num = 1
num++ // num == 2
num-- // num == 1
请留神,与 C 或 JavaScript 不同的是,咱们不能像 ++num
那样将它们前置到数字上。
此外,该操作不会返回任何值。
咱们有布尔运算符帮忙咱们依据虚实值做出决定:&&
、||
和 !
:
true && true // true
true && false // false
true || false // true
false || false // false
!true // false
!false // true
18. 构造
构造体是一种蕴含一个或多个变量的类型。它就像是变量的汇合。咱们称之为字段。它们能够有不同的类型。
上面是定义构造体的代码:
type Person struct {
Name string
Age int
}
请留神,我应用了大写字母作为字段名称,否则这些字段将成为软件包的公有字段,当您将构造体传递给另一个软件包提供的函数(如咱们用于解决 JSON 或数据库的函数)时,就无法访问这些字段。
定义构造体后,咱们就能够用该类型初始化变量:
flavio := Person{"Flavio", 39}
能够应用上面的形式获取构造体的字段数据:
flavio.Age // 39
flavio.Name //"Flavio"
构造体十分有用,因为它能够将不相干的数据分组,并将其传递给函数或从函数中传递进去,还能够存储在片段中,等等。
一旦定义,构造体就是一种相似 int 或字符串的类型,这意味着你也能够在其余构造体外部应用它:
type FullName struct {
FirstName string
LastName string
}
type Person struct {
Name FullName
Age int
}
19.Functions
函数是一个代码块,它被赋予一个名称,并蕴含一些指令。
在 “ 你好,世界!” 示例中,咱们创立了一个 main 函数,它是程序的入口。
package main import "fmt" func main() { fmt.Println("Hello, World!") }
通常,咱们用自定义名称定义函数:
func doSomething() {
而后通过上面的形式调用函数:
doSomething()
函数能够承受参数,咱们必须这样设置参数的类型:
func doSomething(a int, b int) { }
doSomething(1, 2)
a 和 b 是函数外部参数的名称。
函数能够返回一个值,就像上面这样:
func sumTwoNumbers(a int, b int) int {return a + b}
result := sumTwoNumbers(1, 2)
请留神,这里指定了返回值类型
GO 语言的 return 能够返回超过一个值:
func performOperations(a int, b int) (int, int) {return a + b, a - b}
sum, diff := performOperations(1, 2)
这很乏味,因为许多语言只容许一个返回值。函数外部定义的任何变量都是函数的局部变量。
函数也能够承受数量不限的参数,在这种状况下,咱们称之为变量函数:
func sumNumbers(numbers ... int) int {
sum := 0
for _, number := range numbers
{sum + = number}
return sum
}
total := sumNumbers(1, 2, 3, 4)
20. 指针
GO 语言反对应用指针,假如应用上面的变量定义:
age := 20
有了变量指针后,就能够应用 * 运算符获取其指向的值:
age := 20
ageptr = &age
agevalue = *ageptr
这在调用函数并将变量作为参数传递时十分有用。
Go 默认将变量值复制到函数外部,因而不会扭转 age 的值:
func increment(a int) {a = a + 1}
func main() {
age := 20
increment(age) // age is still 20
}
为此,您能够应用指针:
func increment(a *int) {*a = *a + 1}
func main() {
age := 20
increment(& age) // age is now 21
}
21. 函数
一个函数能够调配给一个构造体,在这种状况下,咱们称之为办法。
type Person struct {Name string Age int}
func (p Person) Speak() {fmt.Println("Hello from" + p.Name)
}
func main() {
flavio := Person{
Age: 39,
Name: "Flavio"
}
flavio.Speak()}
办法参数能够指定指针类型或者值类型,这将是一个指针接收器,用于接管指向构造实例的指针:
func (p *Person) Speak() { fmt.Println("Hello from" + p.Name) }
22. 接口
接口是一种定义了一个或多个办法签名的类型。办法没有实现,只有签名:名称、参数类型和返回值类型。相似于这样:
type Speaker interface {Speak() }
当初,你能够让函数承受任何类型,并实现接口定义的所有办法:
func SaySomething(s Speaker) {s.Speak()
}
咱们能够将实现这些办法的任何构造传递给它:
type Speaker interface {Speak()
}
type Person struct {Name string Age int}
func (p Person) Speak() {fmt.Println("Hello from" + p.Name)
}
func SaySomething(s Speaker) {s.Speak()
}
func main() {flavio := Person{ Age: 39, Name: "Flavio"}
SaySomething(flavio)
}
23. 下一步口头
本手册介绍 Go 编程语言。除了这些基础知识,当初还有很多货色须要学习。
垃圾回收、错误处理、并发和网络、文件系统 API 等等。学习无止境。
我的倡议是,抉择一个你想构建的程序,而后开始学习你须要的货色。这会很乏味,也很有播种。
相干
【Linux】《The Command Line Handbook》读书笔记(上半局部)
【Linux】《The Command Line Handbook》读书笔记(下半局部)