Go
Site
- Go 语言中文文档
- Golang 新手可能会踩的 58 个坑
- https://golangguide.top/golang/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF.html
- https://www.gin-vue-admin.com/guide/server/core/viper.html
- https://github.com/spf13/viper
特性
Go(Golang)是一门简洁、高效且独具特色的编程语言,它在语法和设计上与许多其他编程语言有所不同。以下是一些 Go 中的独特使用技巧或特性,这些特性在其他语言中可能不常见或以不同方式实现:
1. 命名返回值
Go 支持在函数声明中为返回值命名,这在其他语言中比较少见。命名返回值可以让代码更清晰,也可以在函数中直接使用这些命名变量。
示例:
func calculate(a, b int) (sum int, product int) {
sum = a + b
product = a * b
return // 直接返回,无需显式列出返回值
}
优点:
- 减少显式定义局部变量的需求。
- 提高代码可读 性,特别是函数有多个返回值时。
注意:
- 不过滥用命名返回值可能会导致代码难以维护,尤其是函数体较长时。
2. 多返回值
Go 原生支持多个返回值,而不需要像其他语言(如 Java 或 Python)那样使用元组或对象包装。
示例:
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
应用场景:
- 常用于返回结果和错误(
result, err的模式在 Go 中非常常见)。 - 避免了使用异常(exceptions)作为错误处理机制。
3. defer 关键字
defer 用于延迟函数的执行,通常用于资源清理(如关闭文件、解锁等)。它的执行顺序是 后进先出(LIFO)。
示例:
func example() {
defer fmt.Println("world")
fmt.Println("hello")
}
// 输出:
// hello
// world
资源清理示例:
func readFile(filename string) {
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer file.Close() // 确保文件在函数退出时被关闭
// 读取文件内容
}
注意:
defer的调用是在函数返回之后、退出之前执行。
4. 简短变量声明(:=)
Go 提供了一种简洁的变量声明方式 :=,可以在函数内快速声明并初始化变量。
示例:
func main() {
x := 10 // 等价于 var x int = 10
y := "hello"
fmt.Println(x, y)
}
注意:
:=只能用于函数内部,不能用于全局变量。- 如果变量已经声明,则
:=不会重新声明,而是更新已有变量的值。
5. 接口(interface)的隐式实现
Go 的接口是隐式实现的,不需要显式声明“实现了某个接口”。只要一个类型实现了接口中定义的所有方法,就认为它实现了该接口。
示例:
type Speaker interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}
func main() {
var s Speaker = Dog{} // Dog 隐式实现了 Speaker
fmt.Println(s.Speak()) // 输出:Woof!
}
优点:
- 减少了模板代码(boilerplate code)。
- 提高了灵活性和解耦性。
6. 零值(Zero Value)
Go 中的变量在声明时会自动初始化为“零值”,即使没有显式赋值。常见的零值如下:
- 数字类型:
0 - 字符串类型:
"" - 布尔类型:
false - 指针、切片、映射、通道、接口:
nil
示例:
var x int // x 的初始值为 0
var s string // s 的初始值为 ""
var b bool // b 的初始值为 false
注意:
- 零值的存在避免了未初始化变量的错误,但同时也要求开发者清楚零值的含义。
7. 切片(Slice)与数组的区别
Go 中的切片(slice)是比数组更强大的数据结构,它是对底层数组的引用,可以动态调整大小。
切片示例:
func main() {
arr := [5]int{1, 2, 3, 4, 5} // 数组
slice := arr[1:4] // 切片,引用了数组的一部分
fmt.Println(slice) // 输出:[2 3 4]
}
切片的特点:
- 切片是动态的,可以扩展或缩小。
- 切片是引用类型,多个切片可以共享同一个底层数组。
8. 内置并发支持(goroutine 和 channel)
Go 原生支持并发编程,通过 goroutine 和 channel 实现。
Goroutine 示例:
func say(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
go say("world") // 启动一个新的 goroutine
say("hello")
}
Channel 示例:
func sum(a, b int, ch chan int) {
ch <- a + b // 将结果发送到 channel
}
func main() {
ch := make(chan int)
go sum(3, 4, ch)
result := <-ch // 从 channel 接收结果
fmt.Println(result) // 输出:7
}
特点:
goroutine是轻量级线程,启动成本很低。channel用于 goroutine 间的通信,避免了显式锁的使用。