Go 1.28 的复合字面量类型省略:整个字面量的类型开始由目标类型推断
这是 Go 1.28 开发期的变更,最终形态以 Go 1.28 release notes 为准。追踪 issue:golang/go#12854。变更汇总页:golang.design/history/runtime-compiler/go1.28.html。
既有规则:只有元素能省
Go 1.27 及以前,复合字面量内部元素的类型可以省略:
[]Point{{1, 2}, {3, 4}}
map[string]Point{"a": {1, 2}}
但字面量本身的类型必须写出来,除非它处在有明确目标类型、且该场景已经被支持的上下文里,比如上面这种嵌套元素的位置。类型信息始终得有一个地方落脚。
#12854 做的事情
issue 把这个「落脚点」放宽了:不再只要求元素位置有目标类型,而是允许由目标类型推断整个复合字面量的类型。线程状态 Done,按 CL 拆开看覆盖面:
17810c616f、54539e7c9e、c7abbff510、73bc5e8ef6:struct literalsc33ea07d92:function callsd87b733482:target may hold any typeb0434e1e9a:target.sigreturns Typef1220f3337:delete08a51e4d9b:append96ef5b334c:copy205624aac7:switch430d19f07a:comparing an untyped composite literal with a typed operand flows the other operand's type in
也就是说,函数调用实参、append/copy/delete 的内建调用位置、switch 的相关位置,都进入了「目标类型可以把整个字面量类型定下来」的范围。后面几个 CL 是在把类型检查器里的 target 表达能力补齐——从「只能装某几类东西」到 any type,再到 target.sig 直接返回 Type。
仍然不行的位置
430d19f07a 留了一条明确的尾巴:比较两侧都是 untyped(或都不是 typed)时,没有可用的目标类型,退回原有机制。这是 Left open 的状态,不要假设这种写法在 1.28 里会被推断出来。
types2 与语法侧的配套
同一线程里,类型检查器的 target-type threading 与上面这些 CL 配套改动。语法侧由 a3edc4c682 加入 untyped composite literals,测试用例在 7ccdc93ad4。语义放宽不是单独一行 parser 改动就能成立的,前端和类型检查要一起走。
对 API 可读性的影响
类型信息从字面量位置移到了目标类型位置,调用点的重复消失了。以前调用一个接受 []Point 的函数,实参里每个 Point 都要写一遍;现在类型只出现在签名一处,读代码时「这是什么类型」的答案从字面量身上转移到了被调用方的签名、变量声明或 switch 的判据上。对库 API 来说,这意味着签名承担了更多表意责任:参数类型写得含糊,调用点就跟着含糊。反过来说,调用点变短之后,读者更依赖编辑器的类型提示和跳转。
迁移工具层要注意什么
- 类型检查器必须以 target-type threading 的新行为为准,否则对省略了类型的字面量会给出错误诊断。
- 语法层要认 untyped composite literals(
a3edc4c682),旧版 parser 直接解析失败,工具链需要设置版本门槛。 gofmt、go vet、gopls这类依赖go/types的工具要整体升级,混合新旧版本时容易出现「编辑器通过、CI 报错」。- 代码生成器与 AST 重写工具如果按「字面量自带类型」的假设做匹配,需要补上目标类型缺失的分支。
同版本但无关的变更
不要混进这次讨论:string(x) 对整数只在 x 是 byte/rune 类型或无类型 rune 常量时允许(#3939),自 1.28 起;recover 在 range-over-func 循环体里可用;range 的第二个变量为空白标识符时跳过求值。