本文共 6184 字,大约阅读时间需要 20 分钟。
点击上方蓝色“Golang来啦”关注我哟
加个“星标”,天天 15 分钟,掌握 Go 语言
via:
https://gist.github.com/prologic/5f6afe9c1b98016ca278f4d507e65510作者:prologic四哥水平有限,如有翻译或理解错误,烦请帮忙指出,感谢!上周五的分享预告点赞已破百,成功解锁了今天的文章。
前几天看到优质文章推送,注意到这篇文章(主要是标题吸引了我),文章非常简洁,让我们一起来回顾下相关的基础知识。
原文如下:
作为经典的 “Hello World”,你的第一个 Go 程序非常简单:
先为我们的项目创建 workspace:
$ mkdir hello$ cd hello
接着创建并初始化 Go module:
$ go mod init hello
使用你最喜欢的编辑器创建 main.go 文件,输入如下代码,并将文件保存在上面创建的 hello 目录下。
package mainimport "fmt"func main() { fmt.Println("Hello World!")}
最后编译产生一个二进制文件:
$ go build
在 workspace 下会生成一个名为 hello 的二进制可执行文件,运行它会输出:
$ ./helloHello World!
在 Go 语言里,你可以使用下面两种方法创建变量:
var x int
其他类型,包括 int、int32、int64、float32、float64、bool和 string 等,还有带 u 前缀的无符号整型类型,例如:uint8 与 byte 是同一种类型(ps:可以认为 type 是 uint8 的别称,见源码 type byte = uint8)
或者创建变量时直接赋值,Go 编译器能自动推导变量类型:
x := 42
通过操作符 = 赋值:
x = 1
注意: 大部分时候你都不需要使用 var 关键字,除非创建变量之后再赋值或者需要使用变量的零值。
使用关键字 func 声明函数:
func hello(name string) string { return fmt.Sprintf("Hello %s", name)}
函数如果有返回值,必须显示地指明返回值类型。
函数可以有多个返回值(一般返回错误和值):
func isEven(n int) (bool, error) { if n <= 0 { return false, fmt.Errorf("error: n must be > 0") } return n % 2 == 0, nil}
函数是 Go 语言的“一等公民”,因此支持很多函数式编程的很多特性,包括闭包、将函数作为参数传递或者返回等,例如:
func AddN(n int) func(x int) int { return func(x int) int { return x + n }}
由于 Go 是一种多范式语言,因此它还通过结构体实现“面向对象”编程,使用关键字 struct 定义结构体:
type Account struct { Id int Balance float64}
结构体字段的定义与变量定义类似,使用操作符 . 访问字段:
account := Account{}fmt.Printf("Balance: $%0.2f", account.Balance)
结构体可以拥有自己的方法。不像其他语言,Go 语言不支持继承也没有类(我们可以通过结构体嵌套实现“继承”)。
type Account struct { id int bal float64}func (a *Account) String() string { return fmt.Sprintf("Account[%d]: $0.2f", a.id, a.bal)}func (a *Account) Deposit(amt flaot64) float64 { a.bal += amt return a.bal}func (a *Account) Withdraw(amt float64) float64 { a.bal -= amt return a.bal}func (a *Account) Balance() float64 { return a.bal}
这些方法称为指针接收者方法,因为第一个参数是指向 Account 结构体的指针。
你还可以像下面这样定义方法:
type Circle struct { Radius float64}func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius}
刚刚定义的是值接收者方法,在方法中对结构体修改不会影响到原结构体,是一份拷贝。
关于结构体、指针接收者方法和值接收者方法可以查看文末推荐阅读了解更多。
与其他语言类似,Go 支持数组,不同的是 Go 语言数组是固定长度的(声明长度之后不支持动态变化),与 C 语言数组类似。
可以指定大小和类型创建数组:
xs := [4]int{1, 2, 3, 4}
大多数场景你会更喜欢使用 slice,有点类似其他语言(比如 Python)中的 list,支持自动扩容。
创建 slice 时可以不用指定大小:
xs := []int{1, 2, 3, 4}
slice 也可以通过 append() 函数创建:
xs := []int{}xs = append(xs, 1)xs = append(xs, 2)xs = append(xs, 3)
可以通过索引范围 slice:
xs[1] // 2
你还可以通过“切片”操作获取数组或者 slice 的子集:
ys := xs[1:] // [2, 3]
可以使用 range 关键字迭代 数组或者 slice:
for i, x := range xs { fmt.Printf("xs[%d] = %d\n", i, x)}
Go 语言里存储键值对的内置数据结构称为 map(类似其他语言的 hash table、hash map、字典或者关联数组)。
可以通过关键字 map 创建 map,需要分别定义键值的类型,比如定义键类型为 string、值类型为 int 的 map:
var counts map[string]int
也可以像下面这样创建 map:
counts := map[string]int{ "Apples": 4, "Oranges": 7,}
类似数组和 slice,也可以通过键访问值:
counts["Apples"] // 4
像数组和 slice 一样,迭代 map:
for key, value := range counts { fmt.Printf("%s: %d\n", key, value)}
唯一需要注意的是,map 使用之前需要先初始化,操作 nil map 将会导致 error 和 panic:
var counts map[string]intcounts["Apples"] = 7 // This will cause an error and panic!
使用之前必须使用 make() 函数初始化:
counts := make(map[string]int)counts["Apples"] = 7
关于数组、slice 和 map 更详细的用法可以查看文末推荐阅读掌握。
Go 语言里面只有一种循环结构,在之前的章节里或许你也看到了:
sum := 0for i := 0; i < 10; i++ { sum += i}
for 循环语句由三部分组成:
初始化语句:第一次迭代之前执行;
条件表达式:每次迭代之前都会执行;
post 语句:每次迭代之后都会执行;
如果忽略循环条件,得到的是无限循环:
for {}// This line is never reached!
Go 语言支持 if-else 语句:
N := 42func Guess(n int) string { if n == 42 { return "You got it!" } else if n < N { return "Too low! Try again..." } else { return "Too high! Try again..." }}
注意:可以忽略最后一个 else,直接像这样返回 return "Too high~ Try again…",两者是等价的。
Go 语言提供的 switch 语句可以代替 if-else,比如:
func FizzBuzz(n int) string { switch n { case n % 15 == 0: return "FizzBuzz" case n % 3 == 0: return "Fizz" case n % 5 == 0: return "Buzz" default: return fmt.Sprintf("%d", n) }}
关键字 defer 可以定义延迟函数,在调用函数返回之前调用,通常在调用函数执行结束之前自动关闭资源(比如文件句柄、数据库连接等),像下面这样:
package mainimport ( "os" "fmt")func Goodbye(name string) { fmt.Printf("Goodbye %s", name)}func Hello(name string) { defer Goodbye(name) fmt.Printf("Hello %s", name)}func main() { user := os.Getenv("User") Hello(user)}
执行会输出:
$ ./helloHello prologicGoodbye prologic
关于 defer 语句的详细使用方法可以阅读文末推荐阅读掌握。
Go 语言里 Errors 代表的是值,比如,使用 os.Open 打开一个文件,如果成功的话会返回一个指向文件的指针和 nil;否则返回 nil 指针和具体的错误。
f, err := os.Open("/path/to/file")
类似其他值类型一样检查错误:
f, err := os.Open("/path/to/file")if err == nil { // do something with f}
Go 语言里,通常的做法是检查函数是否返回非 nil 错误,是的话就直接返回,比如:
func AppendFile(fn, text string) error { f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.WR_ONLY, 0644) if err != nil { return fmt.Errorf("error opening file for writing: %w", err) } defer f.Close() if _, err := f.Write([]byte(text)); err != nil { return fmt.Errorf("error writing text to fiel: %w", err) } return nil}
Go 支持 module(包管理),允许你创建并导入相关包,在刚开始的基本章节中,我们已经知道如何在启动新项目时使用 go mod init 创建模块。
Go 的包仅仅是一个包含源代码目录:
创建 Go 包的第一步是创建一个目录,就像下面这样:
$ mkdir shapes
使用命令 go mod init 初始化:
$ cd shapes$ go mod init github.com/prologic/shapes
现在让我们一起使用你最喜欢的编辑器创建一个 circle.go 模块:
package shapestype Circle struct { Radius float64}func (c Circle) String() string { return fmt.Sprintf("Circle(%0.2f)", c.Radius)}func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius}
需要特别注意的是,包里的函数、结构体、变量和常量的首字母必须是大写才是可导出的;否则即使你导入了相关包也无法访问。
现在,在 Git 上创建名为 shapes 的仓库并 push 你的包代码:
$ git init$ git commit -a -m "Initial Commit"$ git remote add origin git@github.com:prologic/shapes.git$ git push -u origin master
这样的话,你就可以使用全路径 github.com/prologic/shapes 导入 shapes 包,编译时会根据路径自动拉取并构建包。
例如:
我们使用 github.com/prologic/shapes 包做一个例子:
$ mkdir hello$ go mod init hello
编写 main.go:
package mainimport ( "fmt" "github.com/prologic/shapes")func main() { c := shapes.Circle{Radius: 5} fmt.Printf("Area of %s: %0.2f\n", c, c.Area())}
编译:
$ go build
运行二进制文件:
$ ./helloArea of Circle(5.00): 78.50
Congratulations!
ps: 看原文评论,作者反馈接下来还会继续更新原文,我们拭目以待,如果更新的话第一时间给大家分享。
推荐阅读:
结构体、方法:
数组、map 和 slice:
defer:
这是持续翻译的第 13/100 篇优质文章。
如果你有想交流的话题,欢迎留言。如果我的文章对你有所帮助,点赞、转发都是一种支持!
给个[在看],是对四哥最大的支持
转载地址:http://syfnb.baihongyu.com/