Go 类型转换与可赋值性

何时可以直接赋值、何时必须显式转换:可赋值性规则、类型转换语法、无类型常量,以及与类型断言的区别。

#type / concept #status / growing #tech / dev #resource / go

[!info] 关联笔记

Go 类型转换与可赋值性

这个概念为什么出现

Go 几乎不做 C/JS 式数值隐式提升:

var a int32 = 1
var b int64 = 2
// c := a + b // 非法
c := int64(a) + b

必须分清:

  1. 可赋值性:不写转换能否赋值
  2. 类型转换 T(x):是否允许显式变成 T
  3. 类型断言 x.(T):从接口取动态类型(另一专题)

[!abstract] 一句话理解 可赋值性决定“能否直接放入变量/参数”;转换是显式表示变化;断言打开接口看动态类型。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:健康设备把体温从摄氏转到华氏展示

手环上报:

  1. 整数电量百分比 → 图表库要 float64
  2. 体温 Celsius → UI 在部分地区要 Fahrenheit
  3. 协议里的无类型常量 1 写入 byte 状态码

三者都碰到“能不能直接赋值,还是必须 T(x)”。

package main

import "fmt"

// Celsius / Fahrenheit 是不同定义类型,单位不同不能默默互赋。
// 业务意图:避免把 °C 数值直接当 °F 显示。
type Celsius float64
type Fahrenheit float64

// toFahrenheit 演示“单位换算必须走显式转换 + 公式”。
// 教学点:Fahrenheit(c) 只是改类型身份,不会自动换算;
// 真正业务换算要自己写公式(这里用简化演示:先转类型再展示原始数)。
func toFahrenheit(c Celsius) Fahrenheit {
	// 显式转换:底层都是 float64,允许 T(x)。
	// 注意:这不会把 36.5°C 变成 97.7°F;完整公式应是 c*9/5+32。
	// 本示例聚焦“转换语法”,换算公式见注释下一行:
	// return Fahrenheit(float64(c)*9/5 + 32)
	return Fahrenheit(c)
}

func main() {
	// 1) 电量:int → float64,必须显式转换。
	var batteryPct int = 42
	var batteryChart float64 = float64(batteryPct)

	// 2) 体温单位类型之间:不同命名类型,直接赋值非法。
	var bodyC Celsius = 36.5
	// var wrong Fahrenheit = bodyC // 编译错误
	bodyF := toFahrenheit(bodyC)

	// 3) 无类型常量:在可表示范围内可直接赋给 byte。
	const statusOK = 1
	var code byte = statusOK

	fmt.Println(batteryChart, bodyF, code)
}

建议运行:

go run .

期望输出:

42 36.5 1

结合场景再看三个关注点

  1. 可赋值 ≠ 可转换
    同类型/接口/无类型常量等才能直接赋值;intfloat64CelsiusFahrenheit 要写 T(x)

  2. 转换不自动表达业务含义
    Fahrenheit(c) 只换类型标签;真实 °C→°F 还要公式,避免“单位假转换”。

  3. 无类型常量更宽
    const statusOK = 1 能进 byte;一旦 var n int = 1,再赋给 byte 通常就要显式转换。

核心概念

可赋值性

Assignability。常见情形:

  • 类型相同
  • 接口实现关系满足
  • nil 赋给 pointer/func/slice/map/channel/interface
  • 无类型常量可表示为目标类型
  • 未命名类型与底层类型相同的部分情形

type Celsius float64float64 不同,一般需显式转换。

转换 T(x)

Conversions

int(3.9)                 // 3
string(65)               // "A"
string([]byte{'g', 'o'}) // "go"

数值转换可能截断;字符串与字节切片有专门规则。

无类型常量

常量在定型前更“宽”,使 var x float64 = 1 合法。进入变量后的运算通常要求类型一致。

别名 vs 定义类型

type MyInt = int // 别名,可直接当 int
type ID int      // 新类型,需转换

转换 vs 断言

T(x)x.(T)
对象具体值接口值
失败多在编译期panic / ok=false

设计动机

显式转换让截断与单位变化可见;无类型常量保留字面量便利。

边界

  1. 常量 byte(256) 非法;变量回绕是另一套规则。
  2. 接口赋值看方法集。
  3. 不同 []T/[]U 通常不能直接转换。

常见误区

[!warning] 断言当转换
x.(int) 不是 int(x)

[!warning] 期待隐式数值提升
不同整数类型先对齐。

[!warning] 定义类型当别名
type ID intint 不混用。

工程实践

  1. 领域单位用定义类型防混用
  2. API 边界集中转换并校验范围
  3. string/[]byte 转换成本要测量
  4. 表驱动测试覆盖截断

可验证实验

  1. int32 + int 编译否
  2. type A int; A(1) + 2
  3. const c = 256; var x byte = c
  4. 修改 []byte 是否影响已转换 string

本节总结

三线分离:可赋值 / 转换 / 断言。显式是默认。

自测题

  1. 为何 type A int 不能直接赋给 int
  2. nil 可赋给哪些种类?
  3. float64(2)+11 如何定型?
答案
  1. 不同已定义类型。
  2. 指针、函数、slice、map、channel、interface。
  3. 作为无类型常量在 float64 上下文中变为 float64。

延伸阅读


笔记元信息

  • 文件:go-type-conversion-and-assignability.md
  • 状态:已深化
创建于 2026/7/11 更新于 2026/7/15