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)
	}

}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值