




Go中表达式可置于赋值号右边、if条件或函数参数位置,如a + b、len(s);语句如for range则不可,因表达式求值而语句执行动作,二者不可混淆。
Go里最直接的区分标准是:**能不能写在赋值号 = 右边、if 条件里、函数参数位置**。能放进去的,就是表达式;放不进去的,就是语句。
a + b、len(s)、strings.TrimSpace(str) 都是表达式——它们求完值,立刻能用if x > 0 { ... }、for i := 0; i 、return err 全是语句——它们干完事就结束,不“产出”值
x := if true { 1 } else { 2 } 直接报错,因为 if 是语句,不是表达式;想实现类似逻辑,得用三元替代写法(比如封装成函数或用短变量声明)Go对赋值有严格约束:左边得是个“能取地址”的东西,右边必须是个有结果的表达式。这决定了哪些写法合法、哪些看似合理却编译不过。
s[0] = 'a'(切片索引可寻址)、*p = 42(指针解引用可寻址)、x = 1 + 2(右边是表达式)1 + 2 = x(左边不是可寻址操作数)、f() = 10(函数调用不可寻址)、if true { 1 } = 5(if 是语句,不能当左值)const pi = 3.14 定义的是常量,pi = 3.1415 会报 cannot assign to pi,不是因为它是表达式,而是它不可寻址——常量名本身不是左值Go中函数调用是否算表达式,不看它“有没有副作用”,而只看它“有没有返回值”。这个细节影响 map 赋值、通道接收、错误处理等高频场景。
fmt.Sprintf("x=%d", x)、m["key"](map 访问,返回 value 和 bool)、(通道接收,返回接收到的值)
fmt.Println("hello")、ch (发送是语句)、time.Sleep(100 * time.Millisecond)
err := os.Remove("file.txt") 合法,因为 os.Remove 返回 error;但 v := fmt.Print("ok") 报错,因为 fmt.Print 返回 (int, error)?不对——它返回 (int, error),所以其实是多值表达式,但你不能直接 := 给单个变量,得写成 n, err := fmt.Print(
"ok")
Python 或 JavaScript 中,x = 1 既是语句又能当表达式(比如 if (x = 1) {...}),但 Go 彻底切断了这条路。这不是限制,而是防止隐式副作用和歧义。
if、for、switch、select、defer、go —— 它们永远不能嵌入表达式上下文res := func() int { if x > 0 { return 1 } else { return 0 } }(),这是合法的,因为函数字面量本身是表达式,调用它也是表达式for range 当成能返回值的结构,比如想写 keys := for k := range m { ... } —— 不行,for 就是语句,没得商量Go 的设计选择很明确:表达式负责“算出什么”,语句负责“做什么”。混淆二者,往往不是语法记不清,而是试图把别的语言习惯强加给 Go。尤其在重构时,看到一堆 if 块想提成表达式,先停一下——它本就不该是表达式。