Golang | 一日冲刺
今年开始我一点点的开始在生产里面用go语言,但是跟别人聊了以后发现自己还是太过于naive了。所以计划按照费曼学习法把自己学习的过程记录一下。
有人说过其实世界上没有语言,只有特性的集合。go语言基础我打算用一篇博客大概的写一下有什么东西有什么特点,用来给其他博客做铺垫。
有其他语言基础的大神可以快速过一下语法。小白还是乖乖去看书比较好。
基础语法
package
模块划分
go语言主要是由一个个包构成,每个go语言的源文件都以package开始
package xxx
// ......
所有代码按照包来划分功能,一个目录里面的所有代码都属于同一个包。
概念上用法上都类似于c++的namespace或者是Java的package。
go语言入口
和Java类似,go的入口是main函数,但是不是每个包的main函数都可以作为入口,只有main包的main函数(main.main)才是入口。
类似于c runtime,main函数也是被调用的。go的入口其实在runtime包,其中一个goroutine中调用了main.main()
go语言的main函数不带参数,没有返回值。
import
当我们需要用到别人的包的时候,就需要import
pakage main
import "fmt"
如果我们要import多个包,那么我们既可以写多个import,也可以简单一些,用圆括号
package main
import (
"fmt"
"strconv"
)
// ......
函数
定义和调用
go语言里面的函数定义是func <name>(<arglist...>) { <body> }
,调用的话[<package>.]<name>(<arglist...>)
。需要注意,go语言中类型在形参的后面,行末没有分号。
比如说
package main
func Greeting(name string) {
fmt.Printf("Hello, %v", name)
}
func main() {
Greeting("guochao")
}
// Hello, guochao
返回值
go语言支持多返回值,在接受返回值时,变量数量需要和函数返回值数量一致。
pacakge main
func OneRval() int {
return 1
}
func WithError() (int, error) {
return 1, nil
}
func main() {
r1 := OneRval()
r2, err := WithError()
// ...
}
不定参数
go语言允许最后一个参数数量不定,形参写法是
package main
func VAArgsFunc(arg...string) {
// ......
}
类型和变量
go的类型比其他语言略微严格和复杂一些。
大概来分也是primitive types和struct/interface
基础类型
go语言里面不带修饰的类型只有bool byte rune int和float,以及ptr类和channel。
bool跟c++差不多。
对于int和float来说,如果对长度有要求,在后面加上长度,比如说int8 int16 int32 int64 float32 float64。int默认都是有符号的,如果需要无符号,在前面加上u,比如说uint8 uint16。
rune本意符文,玩游戏的大概都知道。在这里主要是指代Unicode中的一个字符。而相对的byte就是单纯的一个字节。后面再具体说。
变量/常量
go语言中类似于func,变量定义可以通过var来定义。相对的常量就是const。语法如下
package main
var aBoolean bool
var initedBoolean = false
var anotherBoolean bool = false
var (
tick = 0
)
const PI = 3.1415926535
:= 和 =
用=赋值一般都没啥问题。赋值的时候类型不一致需要自己进行显式类型转换。
go除了=以外还有一个:=,其实不是赋值,而是在函数中定义本地变量并初始化的另一种写法。 比如说
package main
func main() {
var (
tick = 1
)
// literately equal to
tick := 1
}
需要明确的限制就是,只能在函数中定义并初始化在这个scope中,没有定义过的变量。
type和struct
type类似于c语言的typedef,给类型定义别名的。在go里面定义别名除了好看以外,还有一个用处是作为函数的receiver。这个之后再说。
定义的方法类似与这样
package main
type MyString string
type Item struct {
ID int
name []byte
}
receiver
go语言没有类,但是自己定义的类型可以有receiver,可以让语义更清楚一些,同时也可以用来满足interface的约束
package main
import (
"fmt"
)
type MyString string
func (s MyString) Length() int {
return len(s)
}
func main() {
fmt.Print(MyString("test").Length())
}
// 4
interface
这是一个go语言受争议的特性之一。
go语言里面没有类的概念,取而代之的是interface。更类似于typescript约束参数类型时定义的object interface一些,不过对go来说定义一个接口并且满足它就可以了,不需要明确说我满足了什么interface。更类似于是一种约束,可以在定义函数参数的时候约束传入的实参。
如果需要确认一个类型满足了interface的约束,可以用一个小技巧
大概的语法和struct类似,只不过成员不是变量,而是函数。
pacakge io
type Writer interface{
Write([]byte) (int, error)
}
// 然后实现这个接口
type SringBuilder struct {
// ...
}
func (w *SringBuilder) Write(data []byte) (int, error) {
// ...
}
// 保证这个类型实现了这个接口
var (
_ Writer = &SringBuilder{}
)
匿名函数和函数类型的变量
在go语言中,函数也是一种类型。可以用来赋值或者传参。类似于传递了函数指针
内置函数
go语言像很多其他语言一样,除了标准库,还有一些常用的builtin函数。比如说len、make等等。
选择循环
if
go语言的选择循环都不带括号,比如说if的语法是
if 条件 {
语句
}
pacakge main
func Greeting(name string) {
if len(name) == 0 {
name = "world"
}
fmt.Print("Hello, ", name)
}
func main() {
Greeting("") // Hello, world
Greeting("Chao") // Hello, Chao
}
switch
和其他语言类似的用法
用一个表达式可能的值来进入不同的分支
switch <expr> {
case <possible value>:
...
}
不大一样的地方是
- 不需要break,会自动在下一个case停止
- 如果需要继续执行下一个case,需要fallthrough。
- 如果有多个值进入完全一样的分支,可以在case后面用逗号分隔罗列出所有可能的值
比如说
package main
import (
"os"
)
func main() {
switch len(os.Argv) {
case 1:
//......
fallthrough
case 2:
// ....
case 3, 4:
//......
}
}
用作多个if/else if的简化形式
switch {
case <condition>:
// ......
}
在类型不明时按照类型执行不同的分支
go里面变量可以暂时只标注是某种interface类型,比如说参数接收一个interface的时候,在这个scope内也只能用这个interface的接口。
用goland等IDE看的时候可以看到会有标注。比如说error | github.com/go-errors/errors.Error
,实际上这个变量是errors包的Error类型,但是在这个scope内只是个error
这种情况为了确认具体的类型可以用type switch:
variable := <expr>
switch variable.(type) {
case <type>:
v := variable.<type>
// ......
}
或者等价形式
switch v := <expr>.(type) {
case <type>:
//......
}
如果只是区分类型就不需要给v赋值的部分
for
go里面循环只有一个关键字,但是有多个形式
正常的
类似于c++的普通的for
for <initialization>; <condition>; <statement> {
// ......
}
range for
for v := range <expr> {
// ....
}
代替while
for <condition> {
// .....
}