rune 和 byte 是不同粒度的整数别名:byte 按字节操作,rune 按 Unicode 码点操作;for range 字符串返回 rune,下标访问返回 byte;处理语义字符必须用 []rune,字节流操作用 []byte。

Go 里 rune 和 byte 不是“两种字符类型”,而是两种**不同粒度的整数别名**:一个管“字节”,一个管“字符”。混淆它们,字符串一反转、一截取、一统计长度就出错——尤其处理中文、emoji 或日文时。
为什么遍历字符串得到的是 byte 还是 rune?
因为 Go 的 string 底层是只读的 []byte(UTF-8 编码字节序列),但 for range 遍历时会自动按 UTF-8 编码规则解码成 Unicode 码点,返回的是 rune 和起始字节索引:
-
for i, b := range []byte("你好")→b是byte,每次取 1 字节(结果是228, 189, 160, ...,乱序无意义) -
for i, r := range "你好"→r是rune,每次取 1 个完整字符(r值分别是20320、22909,即“你”“好”的 Unicode 码点) - 直接用下标访问:
s[0]返回byte(第一个 UTF-8 字节),不是第一个字符;想取首字符必须转[]rune(s)[0]
什么时候该用 []byte,什么时候必须用 []rune?
看操作对象是「字节流」还是「语义字符」:
- 用
[]byte:HTTP header 解析、二进制协议打包、文件头校验、base64 编码、bytes.Buffer写入——这些不关心字符含义,只认字节 - 必须用
[]rune:统计字符串“有几个字”(len([]rune(s)))、反转字符串(for i, j := 0, len(runes)-1; i )、按“第3个汉字”截取(string([]rune(s)[2:3])) - 错误示范:
s[:3]对"??abc"可能切在 emoji 中间,产生非法 UTF-8;而string([]rune(s)[:2])才安全
byte 和 rune 的底层本质与转换陷阱
byte 就是 uint8,rune 就是 int32,二者都能参与算术运算,但语义完全不同:
立即学习“go语言免费学习笔记(深入)”;
-
var b byte = 'A'→b == 65(ASCII 值);var r rune = '中'→r == 20013(Unicode 码点) -
string(b)安全(b≤ 127 时是 ASCII 字符);string(r)也安全(rune本就是为 Unicode 设计) - ⚠️ 危险转换:
rune('a')没问题,但byte('世')会 panic:因为'世'是 int32(值 19990),超出了byte的 0–255 范围 - 内存差异明显:
len([]byte("Hello")) == 5,len([]rune("Hello")) == 5;但len([]byte("你好")) == 6(UTF-8 占 3 字节/字),len([]rune("你好")) == 2
最常被忽略的一点:Go 的 string 长度(len(s))永远是字节数,不是字符数。如果你在写日志截断、前端显示省略、或做输入长度限制,却只看 len(s),中文用户输两个字就可能触发 6 字节超限——这时候不转 []rune,逻辑就崩了。










