Go 类型转换与可赋值性
何时可以直接赋值、何时必须显式转换:可赋值性规则、类型转换语法、无类型常量,以及与类型断言的区别。
#concept
#growing
#dev
#go
[!info] related notes
Go 类型转换与可赋值性
这个概念为什么出现
Go 几乎不做 C/JS 式数值隐式提升:
var a int32 = 1
var b int64 = 2
// c := a + b // 非法
c := int64(a) + b
必须分清:
- 可赋值性:不写转换能否赋值
- 类型转换
T(x):是否允许显式变成T - 类型断言
x.(T):从接口取动态类型(另一专题)
[!abstract] 一句话理解 可赋值性决定“能否直接放入变量/参数”;转换是显式表示变化;断言打开接口看动态类型。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:健康设备把体温从摄氏转到华氏展示
手环上报:
- 整数电量百分比 → 图表库要
float64 - 体温
Celsius→ UI 在部分地区要Fahrenheit - 协议里的无类型常量
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 {
// 先做单位换算,再将结果标记为 Fahrenheit。
return Fahrenheit(float64(c)*9/5 + 32)
}
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.Printf("%.0f %.1f %d\n", batteryChart, bodyF, code)
}
建议运行:
go run .
期望输出:
42 97.7 1
结合场景再看三个关注点
-
可赋值 ≠ 可转换
同类型/接口/无类型常量等才能直接赋值;int到float64、Celsius到Fahrenheit要写T(x)。 -
转换不自动表达业务含义
Fahrenheit(c)只换类型标签;真实 °C→°F 还要公式,避免“单位假转换”。 -
无类型常量更宽
const statusOK = 1能进byte;一旦var n int = 1,再赋给byte通常就要显式转换。
核心概念
可赋值性
见 Assignability。常见情形:
- 类型相同
- 接口实现关系满足
nil赋给 pointer/func/slice/map/channel/interface- 无类型常量可表示为目标类型
- 双方底层类型相同、都不是类型参数且至少一方不是命名类型的情形
type Celsius float64 与 float64 不同,一般需显式转换。
转换 T(x)
见 Conversions。
// int(3.9) // 编译错误:常量 3.9 不能表示为 int
f := 3.9
int(f) // 非常量浮点转换,向零截断为 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 | 断言不成立时 panic;双返回值形式为 ok=false |
设计动机
显式转换让截断与单位变化可见;无类型常量保留字面量便利。
边界
- 常量
byte(256)非法;变量回绕是另一套规则。 - 接口赋值看方法集。
- 不同
[]T/[]U通常不能直接转换。
常见误区
[!warning] 断言当转换
x.(int)不是int(x)。
[!warning] 期待隐式数值提升
不同整数类型先对齐。
[!warning] 定义类型当别名
type ID int与int不混用。
工程实践
- 领域单位用定义类型防混用
- API 边界集中转换并校验范围
string/[]byte转换成本要测量- 表驱动测试覆盖截断
可验证实验
int32 + int编译否type A int; A(1) + 2const c = 256; var x byte = c- 修改
[]byte是否影响已转换string
本节总结
三线分离:可赋值 / 转换 / 断言。显式是默认。
自测题
- 为何
type A int不能直接赋给int? nil可赋给哪些种类?float64(2)+1里1如何定型?
答案
- 不同已定义类型。
- 指针、函数、slice、map、channel、interface。
- 作为无类型常量在 float64 上下文中变为 float64。