类型系统
Go 的类型系统解决什么问题?
- 类型系统: 用规则限制一个值可以保存什么数据、参加什么运算以及传给哪些函数;
- 静态类型: 编译器在程序运行前检查类型是否匹配,减少把错误数据交给错误操作的机会;
- 强类型: 不同类型不会因为表示相似就随意混用,需要满足赋值规则或显式转换;
- 类型约束: 变量、参数和表达式受类型规则约束;常量可以暂不绑定具体类型,详见无类型常量;
如何区分类型、值与源码中的字面量?
- type: 一类值的定义,决定表示方式、可用操作和赋值兼容性;
- value: 某个类型的一份具体数据,例如
int类型的值42; - literal: 直接写在源码中的值表示,例如
42、true、"go"; - conversion:
T(v)将允许转换的值变为目标类型;转换规则与数据截断见类型转换; - 转换边界: 类型转换会产生目标类型的值,但不会自动保证精度或业务含义正确;
var count int = 42
ratio := float64(count) / 10
赋值和传参复制什么,为什么副本仍可能共享数据?
-
统一规则: 赋值、传参和返回值都会复制当前值本身,Go 不会自动深拷贝整张对象图;
-
容易混淆的说法: “值类型”和“引用类型”可以帮助入门,但不是 Go 规范对类型的正式二分;
-
真正判断方法: 先问“这个值本身包含数据,还是包含指向其他数据的描述”,再判断复制后的共享效果;
| 类型 | 复制的内容 | 复制后是否可能共享底层数据 |
|---|---|---|
| bool、数值、string | 当前值 | string 不可修改,可按独立值理解 |
| array、struct | 全部元素或字段 | 仅字段内部另含引用时共享 |
| pointer | 地址 | 是 |
| slice | 指针、长度、容量组成的描述 | 是 |
| map、channel、function | 指向运行时状态的值 | 是 |
| interface | 动态类型与动态值 | 取决于其中保存的动态值 |
arrayA := [2]int{1, 2}
arrayB := arrayA
arrayB[0] = 9 // arrayA 仍是 [1 2]
sliceA := []int{1, 2}
sliceB := sliceA
sliceB[0] = 9 // sliceA 也看到 [9 2]
- 设计原则: 需要独立数据时显式复制底层内容;需要共享状态时让共享关系在 API 中可见;