post:完成基本目录的设计,以及前期引导部分的编辑。
This commit is contained in:
48
src/SUMMARY.md
Normal file
48
src/SUMMARY.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Summary
|
||||
|
||||
[版本修订记录](./changelog.md)
|
||||
|
||||
- [一些准备工作](./preguide/preguide.md)
|
||||
- [Go 语言简介](./preguide/intro.md)
|
||||
- [环境安装与配置](./preguide/installation.md)
|
||||
- [不一样的编程思路](./preguide/codex.md)
|
||||
- [基本命令]()
|
||||
- [go 命令]()
|
||||
- [go mod 命令]()
|
||||
- [项目基本结构]()
|
||||
- [创建一个项目]()
|
||||
- [用包来组织代码]()
|
||||
- [可见性]()
|
||||
- [Go 基本语法]()
|
||||
- [基本数据类型]()
|
||||
- [表达式]()
|
||||
- [语句]()
|
||||
- [函数]()
|
||||
- [指针与引用]()
|
||||
- [指针]()
|
||||
- [引用]()
|
||||
- [如何选择]()
|
||||
- [数组与切片]()
|
||||
- [字符串]()
|
||||
- [Map]()
|
||||
- [结构体]()
|
||||
- [接口]()
|
||||
- [泛型]()
|
||||
- [错误处理]()
|
||||
- [反射]()
|
||||
- [并发]()
|
||||
- [协程]()
|
||||
- [通道]()
|
||||
- [并发控制]()
|
||||
- [强大的标准库]()
|
||||
- [日期与时间]()
|
||||
- [IO 处理]()
|
||||
- [文件处理]()
|
||||
- [其他 IO 工具]()
|
||||
- [数学功能]()
|
||||
- [数学运算]()
|
||||
- [上下文]()
|
||||
- [通用数据库处理]()
|
||||
- [网络通信]()
|
||||
- [HTTP 服务]()
|
||||
- [HTML 模板引擎]()
|
11
src/changelog.md
Normal file
11
src/changelog.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 版本修订记录
|
||||
|
||||
这本关于 Go 语言的小书主要是为了入门 Go 语言的初学者使用的。当然它也能为熟练使用 Go 语言的朋友提供一些参考。希望这本小书能帮上你的忙。
|
||||
|
||||
| 作者 | 首次发布 | 最近更新 | 修订版本 | 适用 Go 版本 |
|
||||
| :--: | :-----------: | :-----------: | :------: | :----------: |
|
||||
| 徐涛 | 2023 年 06 月 | 2023 年 06 月 | 1 | 1.20.4 |
|
||||
|
||||
修订版本 1:
|
||||
|
||||
- 整书全新创建。
|
53
src/preguide/codex.md
Normal file
53
src/preguide/codex.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# 不一样的编程思路
|
||||
|
||||
目前世界上比较流行的常用的编程语言,其虽然使用方式不尽相同,但是在了解多了以后,依旧是可以发现其中的共通点的。但是跟 Java、C++这些语言相比,Go 有着其自己独特的设计,这也让 Go 显得十分另类起来。
|
||||
|
||||
Go 语言在整体上是十分简单易用的,但是也常常有很多人在诟病 Go 的设计,觉得 Go 应该更多的吸取其他语言中“成熟”的设计。其实 Go 语言在其设计和发展中并非没有借鉴和参考其他语言中的设计和实现,而是 Go 语言的设计团队有其自己设计哲学。
|
||||
|
||||
## 少即是多
|
||||
|
||||
这一句哲语几乎是 Go 语言设计哲学的核心。我们在实际使用 Go 语言的时候,经常会发现对于一种编程需求的实现,我们没有多少选择,或者说没有其他语言中那种条条大路通罗马的感觉。而少之又少的语言功能,又在很多时候让我们觉得手里的武器捉襟见肘,但又不是完不成任务。
|
||||
|
||||
其实一门编程语言不可能包含所有编程者所期望的特性,能够包含这所有特性的语言一定是一个非常复杂的语言,目前能够做到这一点的语言是 C++,我可以保证任何人看到 C++都会觉得这个语言极其的深奥和复杂,这就是包罗万象的结果。
|
||||
|
||||
Go 语言所采用的方法更加简单:对于一件事情,只提供一种方法,而且会将这种方法做到极致。这就是 Go 的少即是多的原则。这个原则会大大降低开发者的负担,让开发者能够更加专注在所有实现的业务上,而不是需要分神去考虑如何选择语言所的实现。
|
||||
|
||||
所以在使用 Go 语言进行编程的时候,也完全可以遵循这个原则,不必过分的去考虑索要实现的业务功能需要选择怎样的语言特性或者算法特性,直接选择最简单的方法来做就好了。
|
||||
|
||||
## 二八定律
|
||||
|
||||
“百分之八十的代码仅仅使用到了百分之二十的语言特性”,这也是 Go 语言设计者在其工作中总结出来的一个规律。所以在 Go 语言中,语言特性的增加十分缓慢和谨慎,因为语言特性的增加并不能有效显著的提升开发者的效率,但是却会显著的增加语言的复杂性。
|
||||
|
||||
其实这个定律不仅在编程语言的设计中是正确的,在一般的业务项目中也是正确的。大部分的业务项目都存在着过度设计的问题,甚至有一些项目还会为了“炫技”,专门为了设计而设计。这其实都是没有效率的表现。试问,我们在使用 Java 语言设计项目时常用的面向接口的设计方法,在实际的中小型项目中,会有多少情况会真正的被派上用场。因为在绝大多数情况下,一个接口就只有一个实现,这样面向接口的设计方式就有一些过度设计了。也许有人会说,面向接口的设计是为了对未来负责,为未来提供便利,但是大部分项目可能在其整个生命周期里都不会再有什么大的改动了,而且几遍是有改动,可能也会与项目的原始设计差距巨大,提前设计好的接口不一定就一定可以派上用场。这样的话,为什么不在需要的时候再利用局部重构升级所需部分的设计模式呢?
|
||||
|
||||
## 万物皆组合
|
||||
|
||||
与 Java 中的万物皆对象的世界观不同,Go 选择的设计思路是组合。
|
||||
|
||||
继承对于描述世界的思路是集中在一个世界事物中的共通点上,逐步扩大对于这种共通点的描述。但是继承带来的缺点就是,事物是不可能被全面描述的。
|
||||
|
||||
但是组合就不一样了,组合其实更加符合世界的构成规律。例如在微观世界中,原子是由质子、中子和电子组成的,分子又是由原子组成的。而在宏观世界中,一个复杂的大型物体也是由不同的零件组成的,即便是形如一体的人,也是由许多执行不同功能的器官组成的。这些器官又是由不同的组织组成的。所以相比之下,组合比继承更加具有表现力。
|
||||
|
||||
当然组合也不是没有缺点,组合的缺点就是对于代码的组织和维护,要比面向对象的设计方法差很多,在实际项目中需要定义更加严谨的规范来辅助。
|
||||
|
||||
## 静态类型
|
||||
|
||||
动态类型语言现在在市面上的应用大行其道,其简便易用的特性让很多人都对静态类型变得不适应了。但是实际上,静态类型的语言在编译期可以执行更加精确地推断和检查,其所带来的好处就是可以极大的避免错误的发生。
|
||||
|
||||
例如像 Javascript 编写的程序,对于其中很多稀奇古怪的问题我们都会将其归咎为玄学问题,其实归根结底就是 Javascript 所提供的过大的自由度带来的无法被推断的问题,或者说叫不易被推断的问题。
|
||||
|
||||
相比动态类型语言的便捷,静态类型语言虽然牺牲了代码编写的简便,但是提升了运行效率,代码也变得更加易读易懂。
|
||||
|
||||
## 没有异常处理
|
||||
|
||||
没有异常处理并不是没有异常,Go 语言中还是有异常的。不像是 Java、C++语言中使用的`try ... catch`语法结构,Go 中提供的异常处理是`panic`和`recover`的结构。但是这两种结构几乎是完全的不同。
|
||||
|
||||
在使用 Java 语言的`try ... catch`结构的时候,并不是强制的。我们可以选择放任异常的抛出,让程序异常的停下来。或者我们只对我们关心的异常进行了处理,而我们没想到的异常让我们的程序停了下来。
|
||||
|
||||
但是 Go 语言中对于异常的处理则是带有一定强制性的,虽然也可以忽略,但是在实际编程中,只要是碰到了返回的异常,还是需要再三的仔细考虑的。
|
||||
|
||||
## 天生并发
|
||||
|
||||
Go 语言其实就是为并发而生的。对于并发,Go 采用了协程的设计,而不是大多数语言中常见的线程。协程是一种比线程更加轻巧的结构,其创建的时候只使用 4KB 系统内存,这比需要使用 4MB 系统内存的线程已经轻量和非常多了。但是几乎与线程相同的控制模型,有让协程与线程一样易于使用,甚至可能还会更加简单。
|
||||
|
||||
所以,在 Java 或者 C++中,我们开了几百个线程就可能已经系统有些吃不消的时候,Go 可以轻轻松松的创建成千上万的协程。想一想这个处理效率吧。
|
135
src/preguide/installation.md
Normal file
135
src/preguide/installation.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# 环境安装与配置
|
||||
|
||||
Go 语言环境有两种安装方法:安装预编译文件和从源码安装。但是从源码安装并不是构建 Go 语言环境首推的方法,所以这里还是主要介绍安装预编译文件的方法。
|
||||
|
||||
## Windows 系统
|
||||
|
||||
在 Windows 系统中安装 Go 语言开发环境是非常简单的,在 Go 语言官网提供了专门应用于 Windows 平台的二进制安装包。下载以后只需要双击运行即可。
|
||||
|
||||
默认情况下,Go 的二进制文件将会被安装到`C:\Program Files(x86)`目录下面。当然你也可以换一个目录放置,但是这样你就需要在接下来的环境变量配置中使用你所选择的新的目录。
|
||||
|
||||
完成安装以后,如果能够成功执行命令`go version`,那么就基本完成了 Go 语言环境的安装。
|
||||
|
||||
## Linux 系统
|
||||
|
||||
Linux 系统中的 Go 开发环境安装就逼近简单粗暴了,在从 Go 官网下载适用于当前系统的压缩包以后,需要在压缩包所在目录中执行以下命令来完成安装。
|
||||
|
||||
```bash
|
||||
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.4.linux-amd64.tar.gz
|
||||
```
|
||||
|
||||
这个命令将删除系统中之前安装过的 Go 语言环境,然后再将新版本的环境安装进去。这个命令的用法不仅可以用于 Go 语言环境的全新安装,还可以用于 Go 语言环境的升级。
|
||||
|
||||
安装结束以后需要确认一下系统中的`PATH`环境变量的设置,需要将 Go 环境目录引入进来。如果需要设置可以将以下内容写入到系统配置中(通常是`$HOME/.profile`或者`/etc/profile`)。
|
||||
|
||||
```bash
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
```
|
||||
|
||||
与 Windows 系统中的安装确认一样,要确认 Linux 系统中是否已经完成了 Go 语言环境的安装,也同样可以执行`go version`命令,如果能把正常显示所安装的 Go 语言的版本号,那么就说明 Go 语言环境已经基本完成安装了。
|
||||
|
||||
## macOS 系统
|
||||
|
||||
macOS 系统中的 Go 语言环境安装就要简单多了。除了可以像 Linux 系统中那样安装以外,还可以通过 Homebrew 软件管理器来安装。只需要在终端中执行以下命令即可。
|
||||
|
||||
```bash
|
||||
brew install go
|
||||
```
|
||||
|
||||
Homebrew 会自动下载所需要的 Go 的语言环境压缩包,之后会将其解压到其所管理的位置,最后还会自动完成`PATH`环境变量的设置。在一般情况下,当 Homebrew 命令执行结束,`go version`命令就可以正常执行了。
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
Go 语言不管是命令还是程序执行都需要一些环境变量的支持,Go 语言所使用的环境变量可以通过命令`go env`来查看。虽然这个命令会列出很多环境变量,但是其中对我们比较重要的只有少数的几个。
|
||||
|
||||
```admonish tip
|
||||
`go env`命令只能列出系统中目前已经设置的环境变量,不能用来设置新的环境变量。如果需要设置环境变量的值,还是需要到`$HOME/.profile`或者`/etc/profile`文件中。
|
||||
```
|
||||
|
||||
常见的环境变量并且需要设置的环境变量主要有以下几个。
|
||||
|
||||
### `GO111MODULE`
|
||||
|
||||
用于指示 Go 是否开启对`go.mod`文件检测以使用 Module 依赖管理的依据。这个环境变量默认为空,可以设置为`on`来使用 Go 1.13 中正式启用的 Module 依赖分析。设置为`off`可以继续使用 Go 1.13 之前的项目管理风格。
|
||||
|
||||
### `GOPROXY`
|
||||
|
||||
用于设置 Go 的全局代理。默认情况下,Go 会从 Github 下载依赖包,但是对于国内环境来说,Github 并不能保证稳定访问,所以就需要设置这一环境变量来启用 Go 的全局代理。Go 的默认全局代理设置为`https://proxy.golang.org`对于国内来说可以选择以下几个代理:
|
||||
|
||||
- `https://goproxy.cn`,七牛云的代理
|
||||
- `https://mirrors.aliyun.com/goproxy/`,阿里云的代理
|
||||
- `https://goproxy.io/`,官方全球代理
|
||||
|
||||
例如需要设置使用七牛云的代理,只需要这样设置环境变量。
|
||||
|
||||
```bash
|
||||
GOPROXY=https://goproxy.cn,direct
|
||||
```
|
||||
|
||||
通过`GOPROXY`可以设置多个代理,所需要使用的代理只需要使用逗号分开即可。但代理设置的最后一项应该确保为`direct`,这样才能够使 Go 在下载依赖包的时候,能够使用直连的方式访问。如果将`GOPROXY`的值设置为`off`,那么将关闭 Go 对于使用代理服务器的支持。
|
||||
|
||||
### `GOSUMDB`
|
||||
|
||||
常常与`GOPROXY`一起配置的环境变量就是`GOSUMDB`了。`GOSUMDB`环境变量的出现主要是为了解决 Go 在获取依赖包的时候存在的安全问题。在 Go Module 功能被引入之后,包依赖的安全问题就进入了 Go 语言开发团队的视野。Go 在解决依赖包安全的问答题上采用的方法是对每一个依赖包进行校验和(checksum)计算,并将计算得到的校验和记录在一个数据库里,当 Go 下载依赖包以后,也会对下载的依赖包进行校验和计算,并将计算结果与之前记录在数据库中的校验和进行对比。如果对比的结果是一致的,那么就说明下载的依赖包是安全可信的。
|
||||
|
||||
当然 Go 不是在每次编译的时候都会去重新计算校验和,而使会将计算过的校验和保存在项目根目录中一个名为`go.sum`的文件中。这样当项目编译的时候,其所使用的依赖包的校验和与`go.sum`中记录的校验和不一致,那么 Go 就会报出一个提示,来提醒你所下载的内容已经发生了变化。
|
||||
|
||||
环境变量`GOSUMDB`中配置的内容是校验和数据库服务器所在和访问公钥。例如:
|
||||
|
||||
```bash
|
||||
GOSUMDB="gosum.io+ce6e7565+AY5qEHUk/qmHc5btzW45JVoENfazw8LielDsaI+lEbq6"
|
||||
```
|
||||
|
||||
在默认情况下,当`GOPROXY`被设置为`GOPROXY="https://proxy.golang.org"`的时候,`GOSUMDB`就会默认的使用`sum.golang.org`。但是对于身处中国的开发者来说,这个地址依然也是不能稳定访问的,所以,我们要么需要将`GOSUMDB`环境变量设置为`off`,要么就要设置一个可以稳定访问的代理。
|
||||
|
||||
例如常用的校验和数据库地址主要有两个:
|
||||
|
||||
- `sum.golang.google.cn`,这是 Golang 专门为中国推出的校验和数据库镜像。
|
||||
- `gosum.io`,这是 goproxy.io 推出的校验和数据库镜像。
|
||||
|
||||
```admonish tip
|
||||
不推荐在使用`go get`命令下载依赖包的时候使用`-insecure`参数来略过依赖包的安全检测,这是一种非常不安全的行为。
|
||||
```
|
||||
|
||||
### `GOPATH`
|
||||
|
||||
这是一个几乎在任何 Go 语言资料中都可以见到的环境变量,它用来指定 Go 项目工作区的位置,这个工作区中会保存源代码、测试文件、库文件、可执行文件等。
|
||||
|
||||
但是在 Go 1.11 版本引入 Module 特性以后,`GOPATH`环境变量的重要性就降低了。首先在使用 Module 模式开发项目的时候,项目已经不必必须放置在`$GOPATH/src`目录下了,但是`go mod`命令下载的依赖模块依旧会保存在`$GOPATH/pkg/mod`下。
|
||||
|
||||
### `GOROOT`
|
||||
|
||||
`GOROOT`变量在很多 Go 的介绍中都有出现,这个环境变量指定了 Go 的安装目录,如果修改了这个变量的内容,那么将会有很大的可能会使得 Go 命令无法执行。
|
||||
|
||||
```admonish tip
|
||||
如果在修改`GOROOT`以后发生了Go命令找不到而无法使用的情况,那么可以将自己编辑的配置文件中的`GOROOT`环境变量内容删除,即可回复`GOROOT`环境变量的默认值。
|
||||
```
|
||||
|
||||
### `GOMODCACHE`
|
||||
|
||||
`GOMODCACHE`这个环境变量看它的名字就能大概知道这个环境变量是用来定义缓存内容位置的。这个环境变量所定义的内容是 Go 在下载外部依赖文件的时候,存放外部依赖文件的缓存目录。在一般情况下这个环境变量并不用特别的设置,Go 会默认把外部依赖文件下载到`$GOPATH/pkg/mod`下。所以如果更改了`GOPATH`环境变量的值,那么`GOMODCACHE`的内容也会跟随发生变化。
|
||||
|
||||
### `GOPRIVATE`
|
||||
|
||||
在默认情况下,Go 会从公共镜像上下载项目所有的依赖包,并且会对所下载的内容进行安全校验。但是在实际项目开发的时候,并不是所有的代码都是公开的,也就是说有一部分依赖包是保存在私有仓库中的,在这种情况下,通过公共的镜像就不可能访问到这些私有内容了。所以就需要设定`GOPRIVATE`环境变量来告诉 Go,哪些依赖包是需要被作为私有库处理。
|
||||
|
||||
`GOPRIVATE`环境变量可以接受多个值,各个值之间只需要使用逗号分隔开即可。`GOPRIVATE`中可以使用的值的形式只需要能够匹配`path.Match()`即可,换句话说,就是不必在`GOPRIVATE`中逐个列举私有的每一个库,只需要列出其共通的部分即可。例如私有项目通常会放在私有的 Git 库里,所以就可以将私有 Git 库地址放入`GORIVATE`。
|
||||
|
||||
```bash
|
||||
GOPRIVATE=git.archgrid.xyz/go_module
|
||||
```
|
||||
|
||||
在这个示例中,所有存储在`git.archgrid.xyz/go_module`下的模块在获取的时候就不会再走`GOPROXY`设置的代理了。
|
||||
|
||||
### 总结
|
||||
|
||||
在完成 Go 语言环境安装以后,我们可能需要设置的环境变量主要有以下几个。
|
||||
|
||||
```bash
|
||||
GOPATH=/path/to/your/cache/
|
||||
GO111MODULE=on
|
||||
GOPROXY=https://goproxy.io,direct
|
||||
GOSUMDB=gosum.io+ce6e7565+AY5qEHUk/qmHc5btzW45JVoENfazw8LielDsaI+lEbq6
|
||||
```
|
||||
|
||||
完成这几个环境变量的配给,接下来就可以开始 Go 语言程序的编写了。
|
19
src/preguide/intro.md
Normal file
19
src/preguide/intro.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Go 语言简介
|
||||
|
||||
以下是来自 Go 官方网站对于 Go 语言的介绍:“Go 是一种考虑到系统编程而设计的通用语言。它是强类型的和垃圾收集的,并且明确支持并发编程。程序是由包构建的,包的属性允许有效地管理依赖关系。语法紧凑且易于解析,允许通过集成开发环境等自动工具进行轻松分析。”
|
||||
|
||||
Go 的诞生,其主要目的是为了应对逐渐普及的并行计算技术。尤其是在硬件性能普遍支持了并行计算,而编程软件在并行计算方面却一直采用传统的并行处理技术的情况下。在这种技术背景下,Go 语言应时而生,成为了一门专为这个时代硬件诞生的语言。但是这并不代表 Go 会受到时代的局限。
|
||||
|
||||
谷歌公司的三位大神 Robert Griesemer(Java HotSpot 虚拟机、Javascript V8 引擎作者之一)、Rob Pike(贝尔实验室 Unix 团队成员)、Ken Thompson(贝尔实验室 Unix 团队成员,C 语言、Unix 和 Plan 9 创始人之一)为 Go 语言设定了以下目标。
|
||||
|
||||
1. Go 将成为互联网时代的 C 语言,并且跟 C 语言一样足够简单。
|
||||
1. 消除缓慢与笨重、改进低效与扩展性,为开发大型系统设计,为了解决工程上的问题存在。
|
||||
1. 类似于 C 语言,易于学习,拥有现代化的技术。
|
||||
|
||||
基于这三个目标,三位行动派大神迅速展开了对于 Go 的设计工作。从 2007 年开始设计,到 2008 年转为谷歌内部的正式项目,Go 语言最终在 2009 年 11 月 10 日正式开源,而这一天也被确定为 Go 语言的生日。真正能够投入生产使用的 Go version 1 是在 2012 年 3 月发布的。
|
||||
|
||||
但是 Go 1.11 之前的版本并不好用,这并不是因为 Go 语言本身语法和功能设计的问题,而主要是 Go 对于项目依赖的管理问题。与其他的语言不通,Go 直接将 Github 当做了它的依赖存储库,项目的依赖都会从 Github 上直接获取。但是在 Go 1.11 版本之前,Go 在获取依赖的时候,并不能确定依赖库的版本,而是会直接下载依赖的最新版本。这也就导致了,如果在本地有两个依赖于同一个库的不通版本的项目,使用老版本依赖库的项目,必然要在依赖库更新以后不断地做大量的适配重构工作。
|
||||
|
||||
Go 1.11 版本推出了 Go Module 的概念,基本上解决了不同项目之间依赖同一依赖库的不同版本问题,也让 Go 的项目管理变得更加现代化了。Go 1.18 版本加入的泛型,则让 Go 更加的灵活了起来,尤其是不必再遍地使用`interface{}`来指代任意类型的对象。
|
||||
|
||||
这本小书将直接从 Go 1.20 版本开始介绍,所以对于 Go 之前版本中存在的问题和已经不再必要的功能都不再描述了。本书内容将主要以 Go 官网上的语言规范和标准库文档为准,以求尽可能做到对于 Go 的各项功能描述准确。
|
1
src/preguide/preguide.md
Normal file
1
src/preguide/preguide.md
Normal file
@@ -0,0 +1 @@
|
||||
#
|
Reference in New Issue
Block a user