byte是uint8别名,表示字节(0–255);rune是int32别名,表示unicode码点(u+0000至u+10ffff);字符串下标取byte,range遍历得rune;len(s)返回字节数,len([]rune(s))才为字符数。

byte 是字节,rune 是 Unicode 码点
两者本质是不同粒度的整数别名:byte 是 uint8 的别名,取值范围 0–255;rune 是 int32 的别名,能表示所有合法 Unicode 码点(U+0000 到 U+10FFFF)。这意味着 '中' 是合法 rune(值为 20013),但不能赋给 byte 变量——编译会报错,因为 20013 > 255。
字符串下标返回 byte,range 遍历返回 rune
Go 字符串底层是只读的 UTF-8 字节序列,所以 s[0] 拿到的是第一个字节,不是第一个“字符”。对 "你好",s[0] 是 228(0xe4),单独打印是乱码 ä。而 for i, r := range s 中的 r 才是完整解码后的码点,比如 20320(“你”)和 22909(“好”)。
- 想安全取第
n个语义字符?必须先转[]rune(s)[n] - 想读网络包头或写二进制文件?直接用
[]byte(s),零拷贝、不解析 - 误把
range得到的rune当作byte传给函数(如strings.ContainsRunevsbytes.Contains),类型不匹配会编译失败
len(s) 是字节数,len([]rune(s)) 才是字符数
这是最常被忽略的兼容性陷阱。例如 len("你好") == 6(UTF-8 占 3 字节 × 2),但 len([]rune("你好")) == 2。做输入长度限制、日志截断、前端省略显示时,如果只看 len(s),中文用户输两个字就触发“超长”,而实际只写了 2 个字符。
-
s[:10]对含中文的字符串大概率切在某个汉字中间,产生非法 UTF-8 - 正确截前 10 个字符:先
rs := []rune(s),再string(rs[:min(10, len(rs))]) -
[]rune(s)是 O(n) 解码 + 内存分配,大文本(如日志行 >1MB)慎用;若只需遍历,优先for range
转换开销与使用场景完全不对等
[]byte(s) 是零拷贝操作,仅生成切片头;[]rune(s) 必须逐字节解码 UTF-8 并分配新内存。性能差异巨大,但选错类型会导致逻辑错误而非慢——比如反转字符串时直接交换 s[i] 和 s[j],会破坏 UTF-8 结构,输出乱码甚至 panic。
- 处理协议字段、base64、哈希、加密?用
[]byte - 统计 emoji 数量、按“第 5 个字”切分、实现大小写转换?必须用
[]rune - 函数参数该用
byte还是rune?看输入单位:分隔符'|'可用byte;用户昵称首字、过滤表情符号,必须用rune











