Go语言进阶
Go语言进阶
文中大部分示例代码来自Go by Example
Pointers
package main
import (
"fmt"
)
func zeroval(ival int) {
ival = 0
}
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("initial:", i)
zeroval(i)
fmt.Println("zeroval:", i)
// &i 获取变量i的内存地址
zeroptr(&i)
fmt.Println("zeroptr:", i)
fmt.Println("pointer:", &i)
}
Structs
package main
import (
"fmt"
)
// int 的别名
type myInt int
type Person struct {
name string
age int
}
func newPerson(name string) *Person {
p := Person{name: name}
p.age = 21
return &p
}
func printPerson(p Person) {
// 传递一个Person的副本
fmt.Println("p name is ", p.name)
fmt.Println("p age is ", p.age)
}
func changeName(p *Person) {
// 传递一个指针
p.name = "lisi"
}
func main() {
var i myInt = 10
fmt.Printf("type of i is %T\n", i)
var p1 Person
p1.name = "Tom"
p1.age = 22
printPerson(p1)
fmt.Println("================")
changeName(&p1)
printPerson(p1)
fmt.Println(Person{"Bob", 20})
fmt.Println(Person{name: "Alice", age: 30})
fmt.Println(Person{name: "Fred"})
fmt.Println(&Person{name: "Ann", age: 40})
fmt.Println(newPerson("Tony"))
s := Person{name: "Sean", age: 50}
fmt.Println(s.name)
sp := &s
fmt.Println(sp.age)
sp.age = 22
fmt.Println(sp.age)
fmt.Println(s.age)
}
package main
import (
"fmt"
)
// 类名、属性名、方法大小写,大写表示公有的,其他包可以访问,小写表示私有的,本包可以访问
type Student struct {
name string
age int
}
func (s Student) getName() string {
return s.name
}
func (s Student) setName(name string) {
// s 是调用该方法对象的一个拷贝
s.name = name
}
func (s *Student) setName2(name string) {
s.name = name
}
// 方法名大写,表示方法是公有的
func (s Student) Show() {
fmt.Println("s name is ", s.name)
fmt.Println("s age is ", s.age)
}
func main() {
s := Student{name: "tony", age: 22}
fmt.Println(s.getName())
s.setName("lisi")
fmt.Println(s.getName())
s.setName2("tim")
fmt.Println(s.getName())
}
Methods
package main
import (
"fmt"
)
type rect struct {
width, height int
}
// pointer receiver
func (r *rect) area() int {
return r.width * r.height
}
// value receiver
func (r rect) perim() int {
return 2*r.width + 2*r.height
}
func main() {
r := rect{width: 2, height: 3}
fmt.Println("area:", r.area())
fmt.Println("perim:", r.perim())
rp := &r
fmt.Println("area:", rp.area())
fmt.Println("perim:", rp.perim())
}
Interfaces
package main
import (
"fmt"
"math"
)
/*
怎样使用接口
https://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go
*/
// Interfaces are named collections of method signatures.
// 接口是方法签名的集合
type geometry interface {
area() float64
perim() float64
}
// 矩形
type rectangle struct {
width, height float64
}
// 圆
type circle struct {
radius float64
}
/*
To implement an interface in Go, we just need to implement all the methods in the interface.
在Go语言中为了实现一个接口,我们只需要实现接口中所有的方法
*/
// 计算矩形面积
func (r rectangle) area() float64 {
return r.width * r.height
}
// 计算矩形周长
func (r rectangle) perim() float64 {
return 2 * (r.width + r.height)
}
// 计算圆的面积
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
// 计算圆的周长
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
// 测量方法,方法参数为接口类型
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
// interface{} 是万能数据类型
func print(args interface{}) {
// 类型断言,判断参数 args 的数据类型
value, ok := args.(string)
if ok {
fmt.Println("args 的类型是 string", value)
}
fmt.Println(args)
}
func main() {
var g geometry
g = rectangle{width: 1, height: 2}
//g = &rectangle{width: 1, height: 2}
fmt.Printf("%T\n", g)
measure(g)
r := rectangle{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
print("hello")
}
Embedding
package main
import (
"fmt"
)
type base struct {
num int
}
func (b base) describe() string {
return fmt.Sprintf("base with num=%v", b.num)
}
// container 内嵌 base,类似继承
type container struct {
base
str string
}
func main() {
co := container{
base: base{
num: 1,
},
str: "some name",
}
fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)
fmt.Println("alse num:", co.base.num)
fmt.Println("describe:", co.describe())
// 先声明再赋值
var ct container
ct.num = 1
ct.str = "i am ct"
fmt.Println(ct)
type describer interface {
describe() string
}
var d describer = co
fmt.Println("describer:", d.describe())
}
Errors
package main
import (
"errors"
"fmt"
)
/*
查看文章 https://go.dev/blog/error-handling-and-go
*/
func f1(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
}
// A nil value in the error position indicates that there was no error.
return arg + 3, nil
}
// 自定义error
type argError struct {
arg int
prob string
}
// 实现Error方法
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e)
} else {
fmt.Println("f2 worked:", r)
}
}
_, e := f2(42)
// e.(*argError) 判断 e 的数据类型是否为 *argError
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}
Goroutines
package main
import (
"fmt"
"time"
)
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f("direct")
go f("goroutine")
// 匿名函数
go func(msg string) {
fmt.Println(msg)
}("going")
time.Sleep(time.Second * 5)
fmt.Println("done")
}
Channels
package main
import (
"fmt"
"time"
)
func main() {
// 默认channel是没有缓冲的,当接收数据执行的时候才能发送数据
message := make(chan string)
go func() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second * 1)
// 发送ping到channel
message <- "ping"
fmt.Println("send:", i)
}
}()
time.Sleep(time.Second * 10)
// 会阻塞,从channel中接收数据
msg := <-message
fmt.Println(msg)
}
Channel Buffering
package main
import (
"fmt"
"time"
)
/*
带缓冲的 channel
当 channel 已满,再向里面写数据,就会阻塞
当 channel 已空,从里面取数据,也会阻塞
*/
func main() {
// 带缓冲的channel,缓冲2个值
messages := make(chan string, 2)
go func() {
defer fmt.Println("child goroutine exit")
for i := 0; i < 3; i++ {
messages <- fmt.Sprintf("%s%d", "hello-", i)
fmt.Println("message len is ", len(messages), ", cap is ", cap(messages))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ {
fmt.Println(<-messages)
}
fmt.Println("main exit")
}
Channel Synchronization
package main
import (
"fmt"
"time"
)
func worker(done chan bool) {
fmt.Println("working...")
time.Sleep(time.Second * 5)
fmt.Println("done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待worker完成
<-done
}
Channel Directions
package main
import (
"fmt"
)
/*
指定方向的channel
当使用 channel 作为函数参数的时候,可以指定 channel 只能发送或接收数据,这种方式增加了类型安全。
*/
func ping(pings chan<- string, msg string) {
// pings 只能接收数据
pings <- msg
// fmt.Println(<-pings) // 如果试着发送数据,则在编译时就会报错
}
func pong(pings <-chan string, pongs chan<- string) {
// pings <-chan string 只能发送数据
msg := <-pings
// pongs chan<- string 只能接收数据
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "hello")
pong(pings, pongs)
fmt.Println(<-pongs)
}
Select
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
// select 可以监控多个 channel 的状态
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
Timeouts
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()
select {
case msg := <-c1:
fmt.Println("received", msg)
case <-time.After(time.Second * 3):
fmt.Println("timeout")
}
}
Non-Blocking Channel Operations
package main
import (
"fmt"
)
/*
使用select default 语句实现非阻塞发送、接收
*/
func main() {
// 没有缓冲的channel
messages := make(chan string)
// 非阻塞接收
select {
case msg := <-messages:
fmt.Println("received message:", msg)
default:
fmt.Println("no message received")
}
// 非阻塞发送
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message sent")
}
signals := make(chan bool)
select {
case msg := <-messages:
fmt.Println("received message", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}
}
Closing Channels
package main
import (
"fmt"
"time"
)
func main() {
jobs := make(chan int, 5)
// 不带缓冲的channel,会阻塞
done := make(chan bool)
go func() {
for {
// jobs channel 关闭的时候,more返回false
if j, more := <-jobs; more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
time.Sleep(5 * time.Second)
for j := 1; j <= 3; j++ {
fmt.Println("send job", j)
jobs <- j
}
// 发送完数据,关闭channel
close(jobs)
fmt.Println("sent all jobs")
<-done
}
Range over Channels
package main
import (
"fmt"
)
func main() {
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
// 关闭 channel
close(queue)
// 关闭 channel 之后,channel里面的元素继续保留
for elem := range queue {
fmt.Println(elem)
}
}