Go 语言特性

你可能已经注意到------Docker、Kubernetes、etcd、Prometheus、TiDB、CockroachDB......这些云原生时代的明星项目,几乎全是 Go 写的。你刷到过的后端岗位 JD,也越来越多地写着「熟悉 Go 优先」。

但当你真的打开一份 Go 代码,第一反应可能是:

  • := 是个什么鬼?
  • 为什么没有 class,只有 struct
  • 为什么一个 go 关键字就能「并发」,连线程池都不用?
  • 为什么满屏都是 if err != nil
  • 为什么函数能「返回两个值」?

一、认识 Go:它是一门什么样的语言

1.1 它为什么被造出来?

Go(也叫 Golang)诞生于 2007 年的 Google ,由三位大佬联手设计,其中一位是 C 语言的作者 Ken Thompson ,一位是 Unix 和 UTF-8 的重要参与者 Rob Pike

他们造 Go 的初衷很朴素:受够了 C++ 那套又慢又复杂的体系

在 Google,一个稍微大点的 C++ 项目,编译一次要等好几分钟;并发编程要小心翼翼地管理线程和锁;语法又复杂得让人头大。于是他们想造一门新语言,同时满足三个诉求:

  1. 编译要快------像解释型语言一样快,几秒就出结果;
  2. 并发要简单------轻松写出能同时处理海量请求的程序;
  3. 语法要少------让团队里所有人都能很快上手,代码风格高度统一。

1.2 Go 的三大招牌

招牌 含义 一句话理解
简单 语法刻意做「少」,关键字只有 25 个 少,是它的设计目标,不是缺陷
并发 go 一个关键字就能启动协程 并发是「内置能力」,不是「高级技巧」
编译快 静态编译,产物是单个可执行文件 编译后直接丢给服务器就能跑

1.3 一张图看懂 Go 的特性版图

这篇文章要讲的特性,可以分成四大块,先给你一张全景图:
#mermaid-svg-q6M2lgQ5SJ2iuHwD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .error-icon{fill:#552222;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .marker.cross{stroke:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD p{margin:0;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster-label text{fill:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster-label span{color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster-label span p{background-color:transparent;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .label text,#mermaid-svg-q6M2lgQ5SJ2iuHwD span{fill:#333;color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node rect,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node circle,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node ellipse,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node polygon,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .rough-node .label text,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .label text,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape .label{text-anchor:middle;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .rough-node .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape .label,#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape .label{text-align:center;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node.clickable{cursor:pointer;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .arrowheadPath{fill:#333333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster text{fill:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .cluster span{color:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q6M2lgQ5SJ2iuHwD rect.text{fill:none;stroke-width:0;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape p,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .icon-shape .label rect,#mermaid-svg-q6M2lgQ5SJ2iuHwD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q6M2lgQ5SJ2iuHwD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q6M2lgQ5SJ2iuHwD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q6M2lgQ5SJ2iuHwD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go 语言特性
基础语法
核心特性
并发模型
工程工具
变量 / 类型 / 常量
函数 / 指针 / 方法
流程控制 for/if/switch
切片 slice
defer 延迟执行
接口 interface(鸭子类型)
错误处理 error
goroutine 协程
channel 通道
select 多路复用
go mod 依赖
go build/fmt/test

一句话 :Go 的定位很清晰------做基础设施、后端服务、云原生工具的「趁手家伙」。它不求语法花哨,只求「简单、快、并发好」这三件事做到极致。


二、环境与第一个程序:Hello World

2.1 安装与项目初始化

装好 Go 之后,先建一个项目目录,然后初始化模块(Go 用「模块」管理项目,类似给它起个唯一的名字):

bash 复制代码
mkdir hello
cd hello
go mod init example.com/hello
# 会生成一个 go.mod 文件,记录模块名和 Go 版本

2.2 第一个程序逐行拆解

新建 main.go,写上:

go 复制代码
package main        // ① 声明包名:main 包是「可执行程序」的入口

import "fmt"        // ② 导入标准库 fmt(格式化输出)

func main() {       // ③ main 函数:程序的入口
    fmt.Println("Hello, World!")  // ④ 打印一行
}

几个和别的语言不太一样的点,先混个脸熟:

  • package main :每个 Go 文件都归属一个包。只有 main 包里的 main() 函数,才是程序的启动入口。
  • 导入用字符串import "fmt",不用尖括号,也不用分号。
  • func 是 Go 声明函数的唯一关键字。

2.3 go run vs go build

bash 复制代码
go run main.go     # 编译 + 运行,适合开发调试
go build main.go   # 只编译,生成可执行文件(Windows 上是 .exe),适合部署
命令 作用 场景
go run 边编译边运行 快速验证、开发
go build 编译成二进制文件 生产部署

go build 出来的产物是静态编译的单个文件------不依赖目标机器装任何运行时,直接丢上去就能跑。这是 Go 在运维圈特别吃香的重要原因之一。
一句话 :Go 的程序从 package main + func main 开始,用 go run 跑,用 go build 编译成独立的可执行文件。


三、基础语法:变量、类型、常量

3.1 变量声明的两种方式

Go 声明变量,有「完整版」和「简写版」两种:

go 复制代码
// ① 完整版:var 声明,类型写在变量名后面
var age int = 18
var name string = "小明"

// ② 简写版:短变量声明(最常用),类型自动推断
age := 18
name := "小明"

⚠️ 一个重要的坑:=(短变量声明)只能用在函数内部 。包级别(函数外)必须用 var

go 复制代码
package main

var globalCount = 100   // ✅ 包级别,用 var

func main() {
    local := 1          // ✅ 函数内,可以用 :=
}

3.2 基本类型一览

类型 说明 示例
int / int64 整数 42
float64 浮点数(默认) 3.14
string 字符串 "hello"
bool 布尔 true / false
byte 单个字节 'a'
rune 单个 Unicode 字符(中文等) '中'

3.3 Go 特色:零值(zero value)

这是 Go 和很多语言很不一样 的地方------变量声明后,不需要你赋值,它自动有一个「零值」,绝不会是「未定义」的垃圾值:

go 复制代码
var i int          // 0
var s string       // ""(空字符串)
var b bool         // false
var p *int         // nil
var f float64      // 0

#mermaid-svg-pWzMprhswmCALyfK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pWzMprhswmCALyfK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pWzMprhswmCALyfK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pWzMprhswmCALyfK .error-icon{fill:#552222;}#mermaid-svg-pWzMprhswmCALyfK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pWzMprhswmCALyfK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pWzMprhswmCALyfK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pWzMprhswmCALyfK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pWzMprhswmCALyfK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pWzMprhswmCALyfK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pWzMprhswmCALyfK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pWzMprhswmCALyfK .marker.cross{stroke:#333333;}#mermaid-svg-pWzMprhswmCALyfK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pWzMprhswmCALyfK p{margin:0;}#mermaid-svg-pWzMprhswmCALyfK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster-label text{fill:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster-label span{color:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster-label span p{background-color:transparent;}#mermaid-svg-pWzMprhswmCALyfK .label text,#mermaid-svg-pWzMprhswmCALyfK span{fill:#333;color:#333;}#mermaid-svg-pWzMprhswmCALyfK .node rect,#mermaid-svg-pWzMprhswmCALyfK .node circle,#mermaid-svg-pWzMprhswmCALyfK .node ellipse,#mermaid-svg-pWzMprhswmCALyfK .node polygon,#mermaid-svg-pWzMprhswmCALyfK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .rough-node .label text,#mermaid-svg-pWzMprhswmCALyfK .node .label text,#mermaid-svg-pWzMprhswmCALyfK .image-shape .label,#mermaid-svg-pWzMprhswmCALyfK .icon-shape .label{text-anchor:middle;}#mermaid-svg-pWzMprhswmCALyfK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .rough-node .label,#mermaid-svg-pWzMprhswmCALyfK .node .label,#mermaid-svg-pWzMprhswmCALyfK .image-shape .label,#mermaid-svg-pWzMprhswmCALyfK .icon-shape .label{text-align:center;}#mermaid-svg-pWzMprhswmCALyfK .node.clickable{cursor:pointer;}#mermaid-svg-pWzMprhswmCALyfK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pWzMprhswmCALyfK .arrowheadPath{fill:#333333;}#mermaid-svg-pWzMprhswmCALyfK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pWzMprhswmCALyfK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pWzMprhswmCALyfK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pWzMprhswmCALyfK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pWzMprhswmCALyfK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pWzMprhswmCALyfK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pWzMprhswmCALyfK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pWzMprhswmCALyfK .cluster text{fill:#333;}#mermaid-svg-pWzMprhswmCALyfK .cluster span{color:#333;}#mermaid-svg-pWzMprhswmCALyfK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pWzMprhswmCALyfK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pWzMprhswmCALyfK rect.text{fill:none;stroke-width:0;}#mermaid-svg-pWzMprhswmCALyfK .icon-shape,#mermaid-svg-pWzMprhswmCALyfK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pWzMprhswmCALyfK .icon-shape p,#mermaid-svg-pWzMprhswmCALyfK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pWzMprhswmCALyfK .icon-shape .label rect,#mermaid-svg-pWzMprhswmCALyfK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pWzMprhswmCALyfK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pWzMprhswmCALyfK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pWzMprhswmCALyfK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 声明变量

var i int
自动初始化为零值

i = 0
不存在「未初始化」

的脏数据

这个设计能消灭一大类「用了没初始化的变量」的 bug。你永远不用担心读到一个内存里的随机值。

3.4 常量 const 与 iota

go 复制代码
const Pi = 3.14159
const (
    StatusOK   = 200
    StatusFail = 500
)

iota 是 Go 的一个小甜点------在 const 块里,它能自动递增,很适合定义「一组连续的枚举值」:

go 复制代码
const (
    Monday = iota   // 0
    Tuesday         // 1(自动接上)
    Wednesday       // 2
    Thursday        // 3
)

3.5 类型转换(显式,不隐式)

Go 不会自动做类型转换,必须显式写出来:

go 复制代码
var i int = 42
var f float64 = float64(i)   // 必须显式转换

一句话 :Go 的变量用 var:= 声明,类型写在变量名后;变量有「零值」兜底;类型转换必须显式写。这些「反直觉」的细节,都是 Go「把话说清楚」哲学的体现。


四、复合类型:数组、切片、map、结构体

这是 Go 里最常用的几种「装数据的容器」,其中 slice(切片)是重中之重

4.1 数组:定长,其实很少用

go 复制代码
var arr [3]int          // 长度为 3 的数组
arr[0] = 1
arr[1] = 2
arr[2] = 3

// 更常见的写法
arr2 := [3]int{1, 2, 3}

数组的特点是长度固定,长度属于类型的一部分 。因为不够灵活,实际开发中你很少直接用数组,绝大多数场景用的是切片

4.2 切片 slice:Go 的「动态数组」(重点)

切片才是 Go 里真正的「王者容器」,你可以把它理解成「能自动扩容的数组视图」。

创建切片

go 复制代码
// ① 直接字面量
s := []int{1, 2, 3}

// ② make 创建(指定长度,可选容量)
s2 := make([]int, 5)        // 长度 5,容量 5
s3 := make([]int, 0, 10)    // 长度 0,容量 10(预分配)

// ③ 从数组或切片「切」出来
arr := [5]int{1, 2, 3, 4, 5}
sub := arr[1:3]   // [2, 3](下标 1 到 2,左闭右开)

追加元素 append

go 复制代码
s := []int{1, 2, 3}
s = append(s, 4)      // [1, 2, 3, 4]
s = append(s, 5, 6)   // [1, 2, 3, 4, 5, 6]

⚠️ 注意:append 返回一个新切片 ,所以要用 s = append(s, ...) 接住返回值,不能直接 append(s, ...) 就完事。

切片底层长什么样?

这是理解切片的钥匙。一个切片内部由三部分组成:
#mermaid-svg-qd83ZQZv5vjKjj2i{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qd83ZQZv5vjKjj2i .error-icon{fill:#552222;}#mermaid-svg-qd83ZQZv5vjKjj2i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qd83ZQZv5vjKjj2i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qd83ZQZv5vjKjj2i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i .marker.cross{stroke:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qd83ZQZv5vjKjj2i p{margin:0;}#mermaid-svg-qd83ZQZv5vjKjj2i .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster-label text{fill:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster-label span{color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster-label span p{background-color:transparent;}#mermaid-svg-qd83ZQZv5vjKjj2i .label text,#mermaid-svg-qd83ZQZv5vjKjj2i span{fill:#333;color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .node rect,#mermaid-svg-qd83ZQZv5vjKjj2i .node circle,#mermaid-svg-qd83ZQZv5vjKjj2i .node ellipse,#mermaid-svg-qd83ZQZv5vjKjj2i .node polygon,#mermaid-svg-qd83ZQZv5vjKjj2i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .rough-node .label text,#mermaid-svg-qd83ZQZv5vjKjj2i .node .label text,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape .label,#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape .label{text-anchor:middle;}#mermaid-svg-qd83ZQZv5vjKjj2i .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .rough-node .label,#mermaid-svg-qd83ZQZv5vjKjj2i .node .label,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape .label,#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape .label{text-align:center;}#mermaid-svg-qd83ZQZv5vjKjj2i .node.clickable{cursor:pointer;}#mermaid-svg-qd83ZQZv5vjKjj2i .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i .arrowheadPath{fill:#333333;}#mermaid-svg-qd83ZQZv5vjKjj2i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qd83ZQZv5vjKjj2i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qd83ZQZv5vjKjj2i .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qd83ZQZv5vjKjj2i .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qd83ZQZv5vjKjj2i .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qd83ZQZv5vjKjj2i .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster text{fill:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i .cluster span{color:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qd83ZQZv5vjKjj2i .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qd83ZQZv5vjKjj2i rect.text{fill:none;stroke-width:0;}#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape p,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qd83ZQZv5vjKjj2i .icon-shape .label rect,#mermaid-svg-qd83ZQZv5vjKjj2i .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qd83ZQZv5vjKjj2i .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qd83ZQZv5vjKjj2i .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qd83ZQZv5vjKjj2i :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 切片 s

(一个小的结构体)
① 指针

指向底层数组
② len 长度

当前有几个元素
③ cap 容量

底层数组能装几个
底层数组

1,2,3,4,5,...

go 复制代码
s := make([]int, 3, 5)
// len(s) = 3(已有 3 个元素)
// cap(s) = 5(底层数组能装 5 个)

关键点:

  • len 是你「能用到」的元素个数cap 是底层数组「实际分配」的空间;
  • append 超出 cap 时,Go 会分配一块更大的新数组,把老数据拷过去,这就是「扩容」。

切片共享底层数组的坑(重点,一定要懂):

go 复制代码
a := []int{1, 2, 3, 4, 5}
b := a[0:3]   // b 和 a 共享同一个底层数组!

b[0] = 999
fmt.Println(a)   // [999, 2, 3, 4, 5]  ← a 也被改了!

#mermaid-svg-mv6bnCmVMCgyXOpt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mv6bnCmVMCgyXOpt .error-icon{fill:#552222;}#mermaid-svg-mv6bnCmVMCgyXOpt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mv6bnCmVMCgyXOpt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mv6bnCmVMCgyXOpt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt .marker.cross{stroke:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mv6bnCmVMCgyXOpt p{margin:0;}#mermaid-svg-mv6bnCmVMCgyXOpt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster-label text{fill:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster-label span{color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster-label span p{background-color:transparent;}#mermaid-svg-mv6bnCmVMCgyXOpt .label text,#mermaid-svg-mv6bnCmVMCgyXOpt span{fill:#333;color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .node rect,#mermaid-svg-mv6bnCmVMCgyXOpt .node circle,#mermaid-svg-mv6bnCmVMCgyXOpt .node ellipse,#mermaid-svg-mv6bnCmVMCgyXOpt .node polygon,#mermaid-svg-mv6bnCmVMCgyXOpt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .rough-node .label text,#mermaid-svg-mv6bnCmVMCgyXOpt .node .label text,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape .label,#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape .label{text-anchor:middle;}#mermaid-svg-mv6bnCmVMCgyXOpt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .rough-node .label,#mermaid-svg-mv6bnCmVMCgyXOpt .node .label,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape .label,#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape .label{text-align:center;}#mermaid-svg-mv6bnCmVMCgyXOpt .node.clickable{cursor:pointer;}#mermaid-svg-mv6bnCmVMCgyXOpt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt .arrowheadPath{fill:#333333;}#mermaid-svg-mv6bnCmVMCgyXOpt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mv6bnCmVMCgyXOpt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mv6bnCmVMCgyXOpt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mv6bnCmVMCgyXOpt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mv6bnCmVMCgyXOpt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mv6bnCmVMCgyXOpt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster text{fill:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt .cluster span{color:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mv6bnCmVMCgyXOpt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mv6bnCmVMCgyXOpt rect.text{fill:none;stroke-width:0;}#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape p,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mv6bnCmVMCgyXOpt .icon-shape .label rect,#mermaid-svg-mv6bnCmVMCgyXOpt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mv6bnCmVMCgyXOpt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mv6bnCmVMCgyXOpt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mv6bnCmVMCgyXOpt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 共享底层数组
切片 a

len=5
底层数组

999,2,3,4,5

切片 b

len=3

因为 b 只是 a 的一个「视图」,它们指向同一块内存。改 b 会影响到 a这既是切片高效的原因,也是新手最容易踩的坑

4.3 map:键值对

go 复制代码
// 创建(必须用 make 或字面量,不能只 var 声明就塞值)
m := make(map[string]int)
m["apple"] = 1
m["banana"] = 2

// 读取
v := m["apple"]        // 1

// 判断 key 是否存在(重点写法)
v, ok := m["orange"]
if ok {
    fmt.Println("存在,值是", v)
} else {
    fmt.Println("不存在")
}

// 删除
delete(m, "apple")

value, ok := m[key] 这个「双返回值判断存在」的写法,是 Go 的经典惯用法,务必记住。

4.4 结构体 struct

结构体是把多个字段「打包」成一种自定义类型:

go 复制代码
type User struct {
    Name string
    Age  int
}

u := User{Name: "小明", Age: 18}
fmt.Println(u.Name)   // 小明

一句话 :数组定长少用;切片是动态数组 ,记住它底层是「指针 + len + cap」,记住 append 要接返回值、切片共享底层数组;mapvalue, ok 判断存在;struct 用来打包字段。


五、流程控制:for / if / switch / defer

5.1 for:Go 唯一的循环

Go 里没有 while ,一个 for 就能表达所有循环:

go 复制代码
// ① 经典 for
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

// ② 相当于 while
n := 0
for n < 10 {
    n++
}

// ③ 无限循环
for {
    // 配合 break 跳出
}

5.2 if:可以带初始化语句

go 复制代码
// 经典 if
if x > 0 {
    fmt.Println("正数")
} else {
    fmt.Println("非正数")
}

// Go 特色:if 前面可以跟一句初始化,作用域只在 if 内
if err := doSomething(); err != nil {
    fmt.Println("出错了", err)
}

5.3 switch:比想象中灵活

Go 的 switch 有几处「省心」的设计:

go 复制代码
// ① 不用写 break(自动跳出,不像某些语言要手动 break)
switch day {
case 1:
    fmt.Println("周一")
case 2, 3:          // ② 一个 case 可以匹配多个值
    fmt.Println("周二或周三")
default:
    fmt.Println("其他")
}

// ③ 甚至可以不带表达式,直接当 if-else 用
switch {
case score >= 90:
    fmt.Println("优秀")
case score >= 60:
    fmt.Println("及格")
default:
    fmt.Println("不及格")
}

5.4 defer:延迟执行(重点)

defer 是 Go 非常有辨识度的特性------它声明的语句,不会立即执行,而是等「当前函数返回之前」再执行

最常见的用途是保证资源一定被释放,比如关闭文件:

go 复制代码
func readFile() {
    f, err := os.Open("file.txt")
    if err != nil {
        return
    }
    defer f.Close()   // 无论函数怎么返回,Close 都会在最后执行

    // ... 处理文件 ...
}

defer 的两个关键规则

规则一:defer 是「后进先出」(LIFO)------后 defer 的先执行:

go 复制代码
func main() {
    defer fmt.Println("第 1 个 defer")
    defer fmt.Println("第 2 个 defer")
    defer fmt.Println("第 3 个 defer")
    fmt.Println("正常执行")
}
// 输出:
// 正常执行
// 第 3 个 defer
// 第 2 个 defer
// 第 1 个 defer

#mermaid-svg-6CwBOipgt6csDMXa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6CwBOipgt6csDMXa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6CwBOipgt6csDMXa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6CwBOipgt6csDMXa .error-icon{fill:#552222;}#mermaid-svg-6CwBOipgt6csDMXa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6CwBOipgt6csDMXa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6CwBOipgt6csDMXa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6CwBOipgt6csDMXa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6CwBOipgt6csDMXa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6CwBOipgt6csDMXa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6CwBOipgt6csDMXa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6CwBOipgt6csDMXa .marker.cross{stroke:#333333;}#mermaid-svg-6CwBOipgt6csDMXa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6CwBOipgt6csDMXa p{margin:0;}#mermaid-svg-6CwBOipgt6csDMXa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster-label text{fill:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster-label span{color:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster-label span p{background-color:transparent;}#mermaid-svg-6CwBOipgt6csDMXa .label text,#mermaid-svg-6CwBOipgt6csDMXa span{fill:#333;color:#333;}#mermaid-svg-6CwBOipgt6csDMXa .node rect,#mermaid-svg-6CwBOipgt6csDMXa .node circle,#mermaid-svg-6CwBOipgt6csDMXa .node ellipse,#mermaid-svg-6CwBOipgt6csDMXa .node polygon,#mermaid-svg-6CwBOipgt6csDMXa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .rough-node .label text,#mermaid-svg-6CwBOipgt6csDMXa .node .label text,#mermaid-svg-6CwBOipgt6csDMXa .image-shape .label,#mermaid-svg-6CwBOipgt6csDMXa .icon-shape .label{text-anchor:middle;}#mermaid-svg-6CwBOipgt6csDMXa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .rough-node .label,#mermaid-svg-6CwBOipgt6csDMXa .node .label,#mermaid-svg-6CwBOipgt6csDMXa .image-shape .label,#mermaid-svg-6CwBOipgt6csDMXa .icon-shape .label{text-align:center;}#mermaid-svg-6CwBOipgt6csDMXa .node.clickable{cursor:pointer;}#mermaid-svg-6CwBOipgt6csDMXa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6CwBOipgt6csDMXa .arrowheadPath{fill:#333333;}#mermaid-svg-6CwBOipgt6csDMXa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6CwBOipgt6csDMXa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6CwBOipgt6csDMXa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6CwBOipgt6csDMXa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6CwBOipgt6csDMXa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6CwBOipgt6csDMXa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6CwBOipgt6csDMXa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6CwBOipgt6csDMXa .cluster text{fill:#333;}#mermaid-svg-6CwBOipgt6csDMXa .cluster span{color:#333;}#mermaid-svg-6CwBOipgt6csDMXa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6CwBOipgt6csDMXa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6CwBOipgt6csDMXa rect.text{fill:none;stroke-width:0;}#mermaid-svg-6CwBOipgt6csDMXa .icon-shape,#mermaid-svg-6CwBOipgt6csDMXa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6CwBOipgt6csDMXa .icon-shape p,#mermaid-svg-6CwBOipgt6csDMXa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6CwBOipgt6csDMXa .icon-shape .label rect,#mermaid-svg-6CwBOipgt6csDMXa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6CwBOipgt6csDMXa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6CwBOipgt6csDMXa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6CwBOipgt6csDMXa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} defer 第1个
defer 第2个
defer 第3个
函数要返回了
先执行 第3个
再执行 第2个
最后执行 第1个

规则二:defer 会「快照」参数值 ------如果 defer 后面的表达式里有变量,会当场求出值存下来

go 复制代码
func main() {
    x := 1
    defer fmt.Println("x 的值是", x)  // 这里已经把 x=1 记下来了
    x = 100
    // 输出:x 的值是 1(不是 100!)
}

一句话for 一统循环,switch 不用 break 还很灵活,defer 让「善后工作」和「主体逻辑」写在了一起、执行却放在最后。defer 记住两条:后进先出参数快照


六、函数:Go 的一等公民

6.1 函数定义与多返回值

go 复制代码
// 基本函数:func 函数名(参数) 返回类型
func add(a int, b int) int {
    return a + b
}

// 参数类型相同可以合并写
func add2(a, b int) int {
    return a + b
}

Go 函数最鲜明的特色是多返回值------这让「返回结果 + 返回错误」成了标准姿势:

go 复制代码
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("除数不能为 0")
    }
    return a / b, nil
}

// 调用
result, err := divide(10, 0)
if err != nil {
    fmt.Println("出错了:", err)
}

6.2 可变参数

go 复制代码
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

sum(1, 2)        // 3
sum(1, 2, 3, 4)  // 10

6.3 函数是一等公民:可作参数、可作返回值、可有闭包

go 复制代码
// 函数作为参数
func apply(f func(int) int, x int) int {
    return f(x)
}

// 函数作为返回值(闭包)
func counter() func() int {
    count := 0
    return func() int {
        count++       // 闭包:捕获了外部的 count
        return count
    }
}

c := counter()
fmt.Println(c())   // 1
fmt.Println(c())   // 2
fmt.Println(c())   // 3

6.4 命名返回值

返回值可以「先命名」,这样在函数里直接赋值,最后裸 return 即可:

go 复制代码
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return   // 裸 return,自动返回 x、y
}

一句话 :Go 函数最大的特色是多返回值(尤其是「结果 + error」组合),此外它支持可变参数、闭包、函数当参数/返回值,是货真价实的「一等公民」。


七、指针与方法:值类型世界的「引用」

7.1 指针:& 取地址,* 解引用

Go 有指针,但没有指针运算 (不能像某些语言那样 p+1 乱跳),所以既安全又有用:

go 复制代码
x := 10
p := &x       // p 是指向 x 的指针
fmt.Println(*p)  // 10(解引用,取出 x 的值)

*p = 20       // 通过指针修改 x
fmt.Println(x)   // 20

7.2 Go 默认「传值」(重点)

这是理解 Go 的关键心智模型:函数传参,默认是「把值复制一份」传进去,改副本不影响原变量:

go 复制代码
func change(x int) {
    x = 999   // 改的是副本
}

func main() {
    a := 10
    change(a)
    fmt.Println(a)   // 还是 10(没被改)
}

想真正改原变量,就传指针

go 复制代码
func change(x *int) {
    *x = 999   // 通过指针改原变量
}

func main() {
    a := 10
    change(&a)
    fmt.Println(a)   // 999(被改了)
}

7.3 方法 + 接收者:值接收者 vs 指针接收者(重点)

Go 没有类,但可以把函数「绑定」到某个类型上 ,这个被绑定的参数叫「接收者(receiver)」,这样的函数就是「方法」:

go 复制代码
type User struct {
    Name string
}

// ① 值接收者:操作的是副本,改不了原对象
func (u User) SetNameByValue(name string) {
    u.Name = name   // 改的是副本,外面没影响
}

// ② 指针接收者:操作的是原对象,能改
func (u *User) SetName(name string) {
    u.Name = name   // 改的是原对象
}

#mermaid-svg-ptGhAhEQpEhaTFCc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ptGhAhEQpEhaTFCc .error-icon{fill:#552222;}#mermaid-svg-ptGhAhEQpEhaTFCc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ptGhAhEQpEhaTFCc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ptGhAhEQpEhaTFCc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc .marker.cross{stroke:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ptGhAhEQpEhaTFCc p{margin:0;}#mermaid-svg-ptGhAhEQpEhaTFCc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster-label text{fill:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster-label span{color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster-label span p{background-color:transparent;}#mermaid-svg-ptGhAhEQpEhaTFCc .label text,#mermaid-svg-ptGhAhEQpEhaTFCc span{fill:#333;color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .node rect,#mermaid-svg-ptGhAhEQpEhaTFCc .node circle,#mermaid-svg-ptGhAhEQpEhaTFCc .node ellipse,#mermaid-svg-ptGhAhEQpEhaTFCc .node polygon,#mermaid-svg-ptGhAhEQpEhaTFCc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .rough-node .label text,#mermaid-svg-ptGhAhEQpEhaTFCc .node .label text,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape .label,#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape .label{text-anchor:middle;}#mermaid-svg-ptGhAhEQpEhaTFCc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .rough-node .label,#mermaid-svg-ptGhAhEQpEhaTFCc .node .label,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape .label,#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape .label{text-align:center;}#mermaid-svg-ptGhAhEQpEhaTFCc .node.clickable{cursor:pointer;}#mermaid-svg-ptGhAhEQpEhaTFCc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc .arrowheadPath{fill:#333333;}#mermaid-svg-ptGhAhEQpEhaTFCc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ptGhAhEQpEhaTFCc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ptGhAhEQpEhaTFCc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptGhAhEQpEhaTFCc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ptGhAhEQpEhaTFCc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptGhAhEQpEhaTFCc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster text{fill:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc .cluster span{color:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ptGhAhEQpEhaTFCc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ptGhAhEQpEhaTFCc rect.text{fill:none;stroke-width:0;}#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape p,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ptGhAhEQpEhaTFCc .icon-shape .label rect,#mermaid-svg-ptGhAhEQpEhaTFCc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptGhAhEQpEhaTFCc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ptGhAhEQpEhaTFCc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ptGhAhEQpEhaTFCc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指针接收者 (u *User)
传入原对象的地址
方法里改的是原对象
原对象被改
值接收者 (u User)
调用时复制一份
方法里改的是副本
原对象不变

接收者 作用 何时用
值接收者 (u User) 改的是副本 只读、不改字段、小结构体
指针接收者 (u *User) 改的是原对象 要改字段、大结构体(省拷贝)

一句话 :Go 默认「传值」,struct 是值类型,赋值和传参都是「复制一份」。想共享、想修改,就用指针(*)。「值接收者 vs 指针接收者」的差别,本质就是「改副本还是改原对象」。


八、接口 interface:Go 的灵魂(重点)

如果只让你记住 Go 的一个设计,那一定是接口的「隐式实现」。它是 Go 区别于绝大多数语言的地方。

8.1 接口的定义

接口就是「一组方法的约定」,谁实现了这些方法,谁就满足这个接口:

go 复制代码
type Speaker interface {
    Speak() string
}

8.2 隐式实现(鸭子类型)

关键来了------在 Go 里,一个类型不需要显式声明「我实现了某个接口」,只要它的方法「对得上」,就自动满足接口:

go 复制代码
type Dog struct{}

func (d Dog) Speak() string {
    return "汪汪"
}

// Dog 自动满足 Speaker 接口,不需要写 implements!
var s Speaker = Dog{}
fmt.Println(s.Speak())   // 汪汪

这就是所谓的「鸭子类型」哲学:
如果它走路像鸭子、叫起来也像鸭子,那它就是鸭子。
#mermaid-svg-ex4Y1bjKj8R6Yi4t{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .error-icon{fill:#552222;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .marker.cross{stroke:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t p{margin:0;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster-label text{fill:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster-label span{color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster-label span p{background-color:transparent;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .label text,#mermaid-svg-ex4Y1bjKj8R6Yi4t span{fill:#333;color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node rect,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node circle,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node ellipse,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node polygon,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .rough-node .label text,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .label text,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape .label{text-anchor:middle;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .rough-node .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape .label,#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape .label{text-align:center;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node.clickable{cursor:pointer;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .arrowheadPath{fill:#333333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster text{fill:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .cluster span{color:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ex4Y1bjKj8R6Yi4t rect.text{fill:none;stroke-width:0;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape p,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .icon-shape .label rect,#mermaid-svg-ex4Y1bjKj8R6Yi4t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ex4Y1bjKj8R6Yi4t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ex4Y1bjKj8R6Yi4t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ex4Y1bjKj8R6Yi4t :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go:隐式实现
方法对上了,自动成立
Dog 有 Speak() 方法
Speaker
传统语言:显式声明
必须写 implements Speaker
Dog
Speaker

8.3 空接口 interface{}:装任何类型

空接口 interface{} 不约束任何方法,所以任何类型都满足它,可以用来装「任意值」(类似万能容器):

go 复制代码
var anything interface{}
anything = 42
anything = "hello"
anything = Dog{}

Go 1.18 之后,官方推荐用 any 代替 interface{},它们是同一个东西(anyinterface{} 的别名)。

8.4 类型断言:把「万能容器」里的东西取出来

装进去容易,取出来要「断言」回具体类型:

go 复制代码
var v interface{} = "hello"

// ① 类型断言(失败会 panic)
s := v.(string)
fmt.Println(s)   // hello

// ② 带 ok 的安全断言(推荐)
s, ok := v.(string)
if ok {
    fmt.Println("是字符串:", s)
}

8.5 type switch:按类型分流

go 复制代码
func describe(i interface{}) {
    switch v := i.(type) {
    case string:
        fmt.Println("字符串:", v)
    case int:
        fmt.Println("整数:", v)
    default:
        fmt.Println("未知类型")
    }
}

一句话 :Go 的接口是「隐式实现 」------你不需要声明实现关系,只要方法对上了,关系就自动成立。这让接口极其灵活:你甚至能让自己的类型,无缝满足第三方库的接口,而不需要改任何第三方代码。


九、错误处理:error 与 panic/recover

Go 处理错误的方式,和「抛异常」的思路完全不同。

9.1 error 当返回值:显式检查

Go 里,错误是普通的值,通过函数的返回值传回来,调用方显式判断:

go 复制代码
import (
    "errors"
    "fmt"
)

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("除数不能为 0")
    }
    return a / b, nil   // nil 表示「没有错误」
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("出错:", err)
        return
    }
    fmt.Println("结果:", result)
}

9.2 fmt.Errorf 与 %w(错误包装)

go 复制代码
func openConfig() error {
    _, err := os.Open("config.yaml")
    if err != nil {
        // %w 会「包装」原始错误,保留错误链
        return fmt.Errorf("打开配置失败: %w", err)
    }
    return nil
}

9.3 panic / recover:真正的「灾难」

普通错误用 error 返回值处理;而 panic 是「程序遇到了没法继续的情况 」,会立刻崩溃。它配合 defer + recover 可以「接住」崩溃,避免整个程序挂掉:

go 复制代码
func safeRun() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("捕获到 panic:", r)
        }
    }()

    panic("出大事了")
    // 上面的 panic 会被 defer 里的 recover 接住,程序不会崩
}

defer+recover panic 函数体 defer+recover panic 函数体 #mermaid-svg-YWWFsRieFhwzdtDR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YWWFsRieFhwzdtDR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YWWFsRieFhwzdtDR .error-icon{fill:#552222;}#mermaid-svg-YWWFsRieFhwzdtDR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YWWFsRieFhwzdtDR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YWWFsRieFhwzdtDR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YWWFsRieFhwzdtDR .marker.cross{stroke:#333333;}#mermaid-svg-YWWFsRieFhwzdtDR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YWWFsRieFhwzdtDR p{margin:0;}#mermaid-svg-YWWFsRieFhwzdtDR .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YWWFsRieFhwzdtDR text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YWWFsRieFhwzdtDR .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-YWWFsRieFhwzdtDR .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR .sequenceNumber{fill:white;}#mermaid-svg-YWWFsRieFhwzdtDR #sequencenumber{fill:#333;}#mermaid-svg-YWWFsRieFhwzdtDR #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-YWWFsRieFhwzdtDR .messageText{fill:#333;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YWWFsRieFhwzdtDR .labelText,#mermaid-svg-YWWFsRieFhwzdtDR .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .loopText,#mermaid-svg-YWWFsRieFhwzdtDR .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YWWFsRieFhwzdtDR .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-YWWFsRieFhwzdtDR .noteText,#mermaid-svg-YWWFsRieFhwzdtDR .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-YWWFsRieFhwzdtDR .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YWWFsRieFhwzdtDR .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YWWFsRieFhwzdtDR .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YWWFsRieFhwzdtDR .actorPopupMenu{position:absolute;}#mermaid-svg-YWWFsRieFhwzdtDR .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-YWWFsRieFhwzdtDR .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YWWFsRieFhwzdtDR .actor-man circle,#mermaid-svg-YWWFsRieFhwzdtDR line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-YWWFsRieFhwzdtDR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} panic("出大事了")崩溃信号向上抛recover() 接住程序恢复,继续(不会崩)

9.4 三种「出错」手段的边界

机制 用途 会不会崩
error 返回值 预期内的普通错误 不会(由你处理)
panic 不可恢复的严重错误 会(除非 recover)
recover 在 defer 里「接住」panic 阻止崩溃

一句话 :Go 把「普通错误 」和「致命异常 」分开------普通错误当返回值,if err != nil 显式处理,啰嗦但清晰;真正的灾难才 panic。刚开始你会嫌 if err != nil 烦,但习惯了会爱上它的「所见即所得」。


十、并发:goroutine + channel(Go 的王牌)

这是 Go 最值钱的部分,也是它能在云原生时代封神的核心原因。

10.1 goroutine:一个 go 关键字就能并发

启动一个 goroutine(协程),只需要在函数调用前加一个 go

go 复制代码
func say(s string) {
    for i := 0; i < 3; i++ {
        fmt.Println(s)
    }
}

func main() {
    go say("world")   // 开一个协程,并发执行
    say("hello")      // 主协程同时执行
}

没有线程池,没有复杂的配置,go 一个关键字搞定。

goroutine 为什么「轻」?

goroutine 是「用户态协程」,由 Go 运行时调度,而不是直接对应操作系统线程。它通过 GMP 调度模型,把成千上万个协程复用到少量系统线程上:
#mermaid-svg-JCTTRrmRCl5yuNk2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JCTTRrmRCl5yuNk2 .error-icon{fill:#552222;}#mermaid-svg-JCTTRrmRCl5yuNk2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JCTTRrmRCl5yuNk2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .marker.cross{stroke:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JCTTRrmRCl5yuNk2 p{margin:0;}#mermaid-svg-JCTTRrmRCl5yuNk2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster-label text{fill:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster-label span{color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster-label span p{background-color:transparent;}#mermaid-svg-JCTTRrmRCl5yuNk2 .label text,#mermaid-svg-JCTTRrmRCl5yuNk2 span{fill:#333;color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node rect,#mermaid-svg-JCTTRrmRCl5yuNk2 .node circle,#mermaid-svg-JCTTRrmRCl5yuNk2 .node ellipse,#mermaid-svg-JCTTRrmRCl5yuNk2 .node polygon,#mermaid-svg-JCTTRrmRCl5yuNk2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .rough-node .label text,#mermaid-svg-JCTTRrmRCl5yuNk2 .node .label text,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .rough-node .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .node .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape .label,#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape .label{text-align:center;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node.clickable{cursor:pointer;}#mermaid-svg-JCTTRrmRCl5yuNk2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .arrowheadPath{fill:#333333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JCTTRrmRCl5yuNk2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JCTTRrmRCl5yuNk2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster text{fill:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 .cluster span{color:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JCTTRrmRCl5yuNk2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JCTTRrmRCl5yuNk2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape p,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JCTTRrmRCl5yuNk2 .icon-shape .label rect,#mermaid-svg-JCTTRrmRCl5yuNk2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JCTTRrmRCl5yuNk2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JCTTRrmRCl5yuNk2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JCTTRrmRCl5yuNk2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go 运行时调度
G(goroutine 协程)

成千上万个
P(调度器)
M(系统线程)

少量几条
操作系统

所以 Go 能轻松跑百万级并发,而传统线程通常几千个就到头了。

10.2 channel:协程之间通信的「管道」

多个 goroutine 之间怎么交换数据?Go 给出的答案是 channel(通道),而不是「共享内存 + 锁」。

Go 的座右铭是那句著名的话:

不要通过共享内存来通信,而要通过通信来共享内存。

go 复制代码
// 创建一个能传 int 的通道
ch := make(chan int)

// 开一个协程,往通道里发数据
go func() {
    ch <- 42   // 发送 42(<- 表示「送入」)
}()

// 主协程从通道里收数据
value := <-ch   // 接收(<- 表示「取出」)
fmt.Println(value)   // 42

#mermaid-svg-uDarzcezLEZQWfSO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uDarzcezLEZQWfSO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uDarzcezLEZQWfSO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uDarzcezLEZQWfSO .error-icon{fill:#552222;}#mermaid-svg-uDarzcezLEZQWfSO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uDarzcezLEZQWfSO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uDarzcezLEZQWfSO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uDarzcezLEZQWfSO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uDarzcezLEZQWfSO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uDarzcezLEZQWfSO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uDarzcezLEZQWfSO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uDarzcezLEZQWfSO .marker.cross{stroke:#333333;}#mermaid-svg-uDarzcezLEZQWfSO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uDarzcezLEZQWfSO p{margin:0;}#mermaid-svg-uDarzcezLEZQWfSO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster-label text{fill:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster-label span{color:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster-label span p{background-color:transparent;}#mermaid-svg-uDarzcezLEZQWfSO .label text,#mermaid-svg-uDarzcezLEZQWfSO span{fill:#333;color:#333;}#mermaid-svg-uDarzcezLEZQWfSO .node rect,#mermaid-svg-uDarzcezLEZQWfSO .node circle,#mermaid-svg-uDarzcezLEZQWfSO .node ellipse,#mermaid-svg-uDarzcezLEZQWfSO .node polygon,#mermaid-svg-uDarzcezLEZQWfSO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .rough-node .label text,#mermaid-svg-uDarzcezLEZQWfSO .node .label text,#mermaid-svg-uDarzcezLEZQWfSO .image-shape .label,#mermaid-svg-uDarzcezLEZQWfSO .icon-shape .label{text-anchor:middle;}#mermaid-svg-uDarzcezLEZQWfSO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .rough-node .label,#mermaid-svg-uDarzcezLEZQWfSO .node .label,#mermaid-svg-uDarzcezLEZQWfSO .image-shape .label,#mermaid-svg-uDarzcezLEZQWfSO .icon-shape .label{text-align:center;}#mermaid-svg-uDarzcezLEZQWfSO .node.clickable{cursor:pointer;}#mermaid-svg-uDarzcezLEZQWfSO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uDarzcezLEZQWfSO .arrowheadPath{fill:#333333;}#mermaid-svg-uDarzcezLEZQWfSO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uDarzcezLEZQWfSO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uDarzcezLEZQWfSO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uDarzcezLEZQWfSO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uDarzcezLEZQWfSO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uDarzcezLEZQWfSO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uDarzcezLEZQWfSO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uDarzcezLEZQWfSO .cluster text{fill:#333;}#mermaid-svg-uDarzcezLEZQWfSO .cluster span{color:#333;}#mermaid-svg-uDarzcezLEZQWfSO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uDarzcezLEZQWfSO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uDarzcezLEZQWfSO rect.text{fill:none;stroke-width:0;}#mermaid-svg-uDarzcezLEZQWfSO .icon-shape,#mermaid-svg-uDarzcezLEZQWfSO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uDarzcezLEZQWfSO .icon-shape p,#mermaid-svg-uDarzcezLEZQWfSO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uDarzcezLEZQWfSO .icon-shape .label rect,#mermaid-svg-uDarzcezLEZQWfSO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uDarzcezLEZQWfSO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uDarzcezLEZQWfSO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uDarzcezLEZQWfSO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ch <- 42 发送
<-ch 接收
goroutine 1
channel

通道
goroutine 2
数据顺着管道流动

天然避免「抢数据」的竞争

有缓冲 vs 无缓冲

go 复制代码
ch1 := make(chan int)      // 无缓冲:发送方要等接收方(同步)
ch2 := make(chan int, 5)   // 有缓冲:能先塞 5 个,满了才阻塞

关闭与 range

go 复制代码
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)              // 关闭通道

for v := range ch {    // range 会一直读,直到通道关闭
    fmt.Println(v)
}

10.3 select:同时监听多个通道

select 让一个 goroutine 能「同时等待」多个通道,哪个先有数据就先处理哪个:

go 复制代码
ch1 := make(chan string)
ch2 := make(chan string)

go func() { ch1 <- "来自 1" }()
go func() { ch2 <- "来自 2" }()

select {
case msg := <-ch1:
    fmt.Println("收到:", msg)
case msg := <-ch2:
    fmt.Println("收到:", msg)
}

10.4 sync 包:WaitGroup 与 Mutex

虽然「通信共享内存」是主流,但偶尔也需要传统工具:

go 复制代码
import "sync"

// ① WaitGroup:等待一组 goroutine 全部完成
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)          // 计数 +1
    go func(n int) {
        defer wg.Done()   // 完成后计数 -1
        fmt.Println(n)
    }(i)
}
wg.Wait()              // 等所有 goroutine 完成

// ② Mutex:互斥锁,保护共享变量
var mu sync.Mutex
var count int

func inc() {
    mu.Lock()
    count++
    mu.Unlock()
}
工具 作用 一句话
go 启动协程 一个关键字并发
channel 协程通信 通过通信共享内存
select 多路复用 哪个先到先处理哪个
sync.WaitGroup 等待协程完成 计数等待
sync.Mutex 互斥锁 保护共享变量

一句话 :Go 的并发就两个核心------goroutine(轻量协程)负责「跑」,channel(通道)负责「通信」。理解了「通过通信共享内存」这句座右铭,你就理解了 Go 最值钱的那一半。


十一、包与工程:go mod 与工具链

最后,聊聊怎么「组织一个 Go 项目」。

11.1 包与导入

Go 用「包(package)」组织代码,一个目录通常就是一个包:

go 复制代码
// 声明包
package service

// 导入
import (
    "fmt"              // 标准库
    "example.com/myproj/internal/util"   // 自己项目内的包
    "github.com/gin-gonic/gin"           // 第三方包
)

11.2 首字母大小写决定可见性(Go 独特规则)

Go 没有 public / private 关键字,可见性由标识符首字母大小写决定

go 复制代码
type User struct {
    Name string   // 首字母大写 = 导出(其他包能访问)
    age  int      // 首字母小写 = 私有(只有本包能访问)
}

func PublicFunc() {}   // 导出
func privateFunc() {}  // 私有
首字母 可见性 等价概念
大写 导出(exported) public
小写 私有 private

11.3 go mod:依赖管理

go 复制代码
// go.mod 文件示例
module example.com/myproj

go 1.21

require (
    github.com/gin-gonic/gin v1.9.1
)
  • module 声明模块路径(就是项目的唯一名字);
  • require 声明依赖;
  • go.sum 记录依赖的校验和,保证依赖不被篡改。

11.4 工具链:开箱即用

Go 自带一整套工具,不需要额外装任何东西:

命令 作用
go build 编译
go run 直接运行
go test 跑测试
go fmt 自动格式化(强制统一风格)
go vet 静态检查
go mod tidy 自动整理依赖

特别值得一提的是 go fmt------它强制所有 Go 代码的格式一致,从此团队里再也没有「代码风格之争」,这是很多老程序员羡慕到不行的设计。
一句话 :Go 的工程哲学是「工具链全内置 + 约定优于配置 」。不需要额外的构建系统,go buildgo testgo fmt 一条命令搞定;可见性用首字母大小写表达,简单又统一。


写在最后

我们从「Go 到底是什么」出发,一路把它的语法和特性系统过了一遍。回顾这条主线:

  1. 认识 Go:一门「简单、并发好、编译快」的语言,云原生的基础设施语言;
  2. 入门package main + func main 起步,go run / go build 一跑一编译;
  3. 基础语法var / := 声明变量,变量有「零值」兜底,类型转换显式写;
  4. 复合类型 :数组定长少用,切片 slice (指针+len+cap)才是王者,mapvalue, ok 判存在;
  5. 流程控制for 一统循环,switch 不用 break,defer 后进先出、参数快照;
  6. 函数:多返回值是招牌,函数是一等公民(闭包、当参数);
  7. 指针与方法:Go 默认传值,想共享就传指针,值/指针接收者分清;
  8. 接口 interface:隐式实现(鸭子类型),Go 最惊艳的设计;
  9. 错误处理error 当返回值,panic/recover 处理灾难;
  10. 并发goroutine 跑 + channel 通信,Go 的王牌;
  11. 工程go mod 管依赖,首字母大小写定可见性,工具链全内置。
相关推荐
XuCoder21 分钟前
你以为隔离级别只是个开关,可 MySQL 凭什么靠它挡住幻读?
后端
创新技术阁21 分钟前
FastapiAdmin 前后端启动全流程详解
前端·后端·fastapi
七牛开发者27 分钟前
实测推荐 3 个 Skill,轻松上手 Codex 网页设计与交付流程
前端·javascript·后端
七牛开发者30 分钟前
拆解 dsh 系列:从源码和版本变化看 DeepSeek Harness 的设计取舍
前端·javascript·后端
luteres42 分钟前
Spring学习笔记
java·后端·spring
深念Y43 分钟前
登录日志与管理员审计日志存储决策
前端·arm开发·后端·微服务·云原生·架构
INGNIGHT1 小时前
1908 · 布尔表达式求值(stack单调栈&dfs)
开发语言·c++·算法
এ慕ོ冬℘゜1 小时前
深入理解浏览器全屏 API:从 requestFullscreen 到全屏实战
开发语言·html
用户8356290780511 小时前
使用 Python 为 PowerPoint 演示文稿添加评论
后端·python