Go

引言

非常入门的一本书?准确来说是一篇GO的入门文章,用于学学英语和简单了解GO还不错,并且词汇和语法都比较简洁易懂。

话不多说,下面就来看看这篇文章的翻译。

原文

https://www.freecodecamp.org/news/go-beginners-handbook/

1.介绍GO

它是由谷歌工程师创建的,设计这门语言的主要目标是:

  • 提高项目的编译和运行速度
  • 入门门槛足够低,但是也要避免过于简单和低级。
  • 具备可移植(编译后的 Go 程序是二进制文件,不需要其他文件就能运行,而且是跨平台的,因此可以很容易地分发)
  • 单调、稳定、可预测,犯错的机会少 。
  • 能充分发挥多线程优势

2.如何开始?

在深入了解该语言的具体内容之前,您应该了解以下几件事。

首先,https://go.dev 是该语言的主页。

下面是首选的入门资源:

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

image.png

5.你好,世界

  1. 创建一个新的文件夹,比如下面的 hello。

image.png

  1. 创建一个hello.go的文件,并且在文件内写入下面的内容:
package main 

import "fmt" 

func main() { 
    fmt.Println(" Hello, World!") 
}
  1. 在文件所在路径,运行go run hello.go,如果结果如下说明运行成功:
xander@LAPTOP-47J243NL MINGW64 /e/adongstack/go/hello

$ go run hello.go
 Hello, World!

下面来解释下上面的入门程序:

  1. 每个 .go 文件首先声明它是哪个软件包的一部分
  2. 一个软件包可以由多个文件组成,也可以只由一个文件组成。一个程序可以包含多个软件包。
  3. main函数是程序的入口点,也是可执行程序的标识。
  4. 我们使用 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 是可移植的,因为本质编译之后本质就是二进制文件,这样每个人都可以按原样运行程序,并且二进制文件已经打包供执行。

程序将在我们构建它的相同架构上运行。

我们可以使用 GOOSGOARCH 环境变量为不同的架构创建不同的二进制文件:

GOOS = windows GOARCH = amd64 go build hello.go

这将为 64 位 Windows 机器创建一个 hello.exe 可执行文件:

image.png

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 下:

image.png

当你使用 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”:

image.png

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 中会有相关提示。

image.png

这些错误信息实际是编译

image.png

如果您声明一个变量,但没有将其初始化为一个值,那么它会自动分配一个值,这个值取决于变量的类型,例如整数为 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》 读书笔记(下半部分)


Xander
198 声望51 粉丝