跳到主要内容

类型系统

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 中可见;