// globalVar := 10 // 错误!全局变量只能用 var 声明 var globalVar = 10
funcmain() { a := 1// 声明新变量 a a = 2// 改变已存在的变量 a,使用 = // a := 3 // 错误!a 已经声明过 b, a := 3, 4// 正确!因为 b 是新变量,a 退化为赋值操作 fmt.Println(globalVar, a, b) }
7. 作用域问题
Go 使用词法作用域(块作用域)。内层大括号可以直接访问外层变量;如果在内层大括号里使用 := 重新声明了同名变量,会发生变量遮蔽,此时内层操作不会影响外层。 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
package main
import"fmt"
funcmain() { x := 1 fmt.Println(x) // prints 1 { // 大括号圈定了代码块中声明的变量的作用域 fmt.Println(x) // prints 1 (访问外层) x := 2// 遮蔽:声明了一个全新的局部变量 x,不会影响到外部 x fmt.Println(x) // prints 2 } fmt.Println(x) // prints 1 (外层 x 未被改变) }
8. Nil 不能赋给未指定类型的变量
nil 本身没有默认类型,必须有一个具体的上下文(如指针、接口、切片)。如果你直接声明 var x = nil 或者 x := nil 编译器将无法推断类型。 代码:
1 2 3 4 5 6 7 8 9 10 11
package main
import"fmt"
funcmain() { // x := nil // 编译错误:use of untyped nil
// 正确做法:显式指明为可以接收 nil 的类型,如 interface{} var x interface{} = nil fmt.Println(x) }
9. 类型转换
Go 是强类型语言,不存在隐式类型转换,哪怕是 int 和 int64 之间也需要显式转换。字符串与数字互转必须借助 strconv 包。 代码:
funcmain() { var i int var b bool var s string // if i == nil {} // 编译报错 if i == 0 { fmt.Println("int 零值为 0") } if b == false { fmt.Println("bool 零值为 false") } if s == "" { fmt.Println("string 零值为空字符串") } }
// 结构体作为 Map 值的陷阱 stObj := st{A: 1, B: 2, C: 3} mp2 := map[int]st{1: stObj} // mp2[1].A = 10 // 编译报错:cannot assign to struct field mp2[1].A in map s := mp2[1] s.A = 10// 仅仅修改了副本 s fmt.Println(mp2[1].A) // 输出仍是 1 (未改变) // 正确的做法是修改后重新赋给 map,或者 Map 值使用结构体指针: mp2[1] = s // 放回 map 中 }
18. Go 包依赖与 Module 管理
Go 1.11 后引入了 Go Module,抛弃了原来繁琐的 GOPATH 机制。使用外部依赖前,必须进行模块初始化和下载。 命令:
1 2 3 4
go mod init my_project # 初始化项目,生成 go.mod go get github.com/gin-gonic/gin # 下载依赖包,并更新 go.mod 和 go.sum go install xxx # 编译并安装二进制可执行文件到 GOPATH/bin go mod tidy # 自动整理依赖,清理没用到的包,下载缺失的包
funcdoIt(workerID int, ch <-chaninterface{}, done <-chanstruct{}, wg *sync.WaitGroup) { fmt.Printf("[%v] is running\n", workerID) defer wg.Done() // 通知执行完毕 for { select { case m, ok := <-ch: if ok { fmt.Printf("[%v] m => %v\n", workerID, m) } case <-done: // 只要接收到关闭信号 fmt.Printf("[%v] is done\n", workerID) return } } }
funcmain() { var wg sync.WaitGroup done := make(chanstruct{}) ch := make(chaninterface{}, 10) workerCount := 10
for i := 0; i < workerCount; i++ { wg.Add(1) // wg 必须传指针,否则 doIt 内部调用 Done 只会改变副本,导致死锁 go doIt(i, ch, done, &wg) }
// 主进程充当生产者,向通道中发送消息 for i := 0; i < workerCount; i++ { ch <- i }