len函数只能用于数组、切片、字符串、map和channel;对nil切片或nil map调用合法且返回0;字符串len返回UTF-8字节数,非Unicode字符数;[]rune可获取真实字符数但有性能开销。

len 函数能直接获取哪些类型的长度
len 是 Go 内置函数,不是方法,只能用于编译期已知长度的类型:数组([5]int)、切片([]string)、字符串(string)、map(map[string]int)、channel(chan int)。它不能用于结构体、指针、函数或自定义类型(除非底层类型是上述之一)。
常见错误是试图对 nil 切片或 nil map 调用 len —— 实际上这完全合法:len(nil切片) 返回 0,len(nil map) 也返回 0。真正 panic 的是取值(如 m["k"])或遍历(for range nil map)。
-
len("你好")返回 6(字节数,UTF-8 编码) -
len([]rune("你好"))返回 2(Unicode 码点数) -
len([3]int{1,2,3})返回 3(数组长度固定) -
len(map[int]string{})返回 0(空 map)
字符串 len 返回的是字节数,不是字符数
Go 字符串底层是只读字节序列,len(s) 永远返回 UTF-8 编码后的字节数。中文、emoji 等多字节字符会拉高这个值,但不反映“人眼看到的字符个数”。
如果业务需要按 Unicode 字符计数(比如截断显示、校验用户名最大 10 个汉字),必须转成 []rune:
立即学习“go语言免费学习笔记(深入)”;
name := "Hello世界?" fmt.Println(len(name)) // 输出: 13(H-e-l-l-o-世-界-? 各自 UTF-8 字节数之和) fmt.Println(len([]rune(name))) // 输出: 9(5 ASCII + 2 汉字 + 1 emoji = 9 个 rune)
注意:[]rune(s) 会分配新底层数组,对超长字符串(如 MB 级日志)频繁调用有性能开销。
切片和数组的 len 行为差异
数组长度是类型的一部分,len 在编译期确定;切片长度是运行时属性,可变。
-
arr := [4]int{1,2,3,4}; len(arr)→ 永远是 4,不可修改 -
sl := []int{1,2,3}; sl = sl[:2]; len(sl)→ 变为 2 -
len(sl)和cap(sl)可能不同:切片可能只用了底层数组一部分
误把 cap 当 len 用会导致逻辑错误,比如循环写满整个容量而非当前长度:
data := make([]byte, 0, 1024)
data = append(data, 'a', 'b')
// 错误:for i := 0; i < cap(data); i++ { ... } —— 会访问未初始化内存
// 正确:for i := 0; i < len(data); i++ { ... }
map 和 channel 的 len 使用场景与陷阱
len(m) 返回当前键值对数量,len(ch) 返回当前缓冲区中待读取元素个数。两者都常用于条件判断,但要注意并发安全。
- map 的
len不是原子操作,多 goroutine 写入时,即使只读len也可能触发 panic(Go 1.19+ 对并发读写 map 的 panic 更敏感) - channel 的
len(ch)是瞬时快照,无法替代select或range做同步控制 - 不要用
len(ch) == cap(ch)判断是否满 —— 非缓冲 channel 的cap是 0,len也是 0,永远相等
真正需要长度感知的并发场景,应配合 sync.Map、互斥锁或 channel 自身的阻塞语义来设计,而不是依赖 len 做轮询判断。
最容易被忽略的是:字符串的 len 和 []rune 的 len 在语义上完全不同,而 Go 不提供类似 Python 的 len(s, encoding='utf8') 这种带选项的接口 —— 开发者必须自己决定用字节还是 rune,并承担转换成本。










