函数签名: 输入与输出参数类型列表。
函数类型定义:
type Printer func(content string,str string) (n int, err error)
type Printer func(string, string) (int, error)
type Printer func(content string, id int) (n int, err error)
type Printer1 func(string, int) (int, error)
type Printer2 func(int, string) (int, error)
func main() {
var printer Printer
printer = echo
printer("printer", 0)
var printer1 Printer1
printer1 = echo
printer1("printer1", 1)
//var printer2 Printer2
//printer2 = echo//不能通过编译,类型不同
}
func echo(content string, id int) (n int, err error) {
fmt.Println(content, id)
return 0, nil
}
满足以下任一条件即是高级函数:
实现一个函数,该函数操作两个整数,具体是什么操作由调用方实现.
方案一: 输入参数中输入函数实现
type operate func(x, y int) int
func calculate(x int, y int, op operate) (int, error) {
if op == nil {
//卫述语句
return 0, errors.New("invalid operation")
}
return op(x, y), nil
}
方案二: 利用输出函数值实现
type calculateFunc func(x int, y int) (int, error)
func genCalculator(op operate) calculateFunc {
return func(x int, y int) (int, error) {
if op == nil {
return 0, errors.New("invalid operation")
}
return op(x, y), nil
}
}
此种高级函数实现了闭包函数, genCalculator中的匿名函数即是闭包函数. 因为该函数引用了自由变量op.
如果一个字段的声明中只有字段的类型名而没有字段的名称,那么它就是一个嵌入字段,也可以被称为匿名字段。
值类型
指针类型
给接口变量赋值时,接口变量会持有被赋予值的副本,而不是它本身.
接口变量的值包含两个指针,一个指向动态值,一个指向类型信息。
嵌入接口
同一接口内接口的方法与嵌入接口之间、各个嵌入接口之间不允许有同名的方法, 编译报错
不要通过共享数据来通讯,恰恰相反,要以通讯的方式共享数据。
goroutine代表着并发变成模型中的用户级线程。
以下代码输出信息是?
func main() {
for i := 0; i < 10; i++ {
go func() {
fmt.Printf("%p,%d\n", &i, i)
}()
}
fmt.Println("hello")
}
方法一:使用time.Sleep
func method1() {
num := 10
for i := 0; i < num; i++ {
go func() {
fmt.Println("method1", i)
}()
}
time.Sleep(time.Millisecond * 500)
fmt.Println(">>>method1 is done")
}
方法二:使用通道
func method2() {
num := 10
sign := make(chan struct{}, num)
for i := 0; i < num; i++ {
go func() {
fmt.Println("method2", i)
sign <- struct{}{}
}()
}
for j := 0; j < num; j++ {
<-sign
}
fmt.Println(">>>method2 is done")
}
方法三:使用syn.WaitGroup
func method3() {
num := 10
wg := sync.WaitGroup{}
for i := 0; i < num; i++ {
wg.Add(1)
go func() {
defer func() {
wg.Done()
}()
fmt.Println("method3", i)
}()
}
wg.Wait()
fmt.Println(">>>method3 is done")
}
如何限制routine的个数
func main() {
var maxRoutineNum = 5
ch := make(chan struct{}, maxRoutineNum)
for i := 0; i < 10; i++ {
ch <- struct{}{}
go func(v int) {
fmt.Println(v)
time.Sleep(time.Second)
<-ch
}(i)
}
fmt.Println("hello")
time.Sleep(100000)
}
主要思想: 设置全局信号,接受到对应信号时执行,反之则等待.
func main() {
numbers1 := []int{1, 2, 3, 4, 5, 6}
for i := range numbers1 {
fmt.Println(i)
}
fmt.Println()
}
func arrayRange() {
numbers2 := [...]int{1, 1, 1, 1, 1, 1}
maxIndex2 := len(numbers2) - 1
for i, e := range numbers2 {
if i == maxIndex2 {
numbers2[0] += e
} else {
numbers2[i+1] += e
}
}
fmt.Println(numbers2)
fmt.Println()
}
func sliceRange() {
numbers2 := []int{1, 1, 1, 1, 1, 1}
maxIndex2 := len(numbers2) - 1
for i, e := range numbers2 {
if i == maxIndex2 {
numbers2[0] += e
} else {
numbers2[i+1] += e
}
}
fmt.Println(numbers2)
fmt.Println()
}
func main() {
value1 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch 1 + 3 { // 这条语句无法编译通过。
case value1[0], value1[1]:
fmt.Println("0 or 1")
case value1[2], value1[3]:
fmt.Println("2 or 3")
case value1[4], value1[5], value1[6]:
fmt.Println("4 or 5 or 6")
}
}
上述代码不能编译通过,因为switch中的值类型为int,而case中的类型为int8, 值类型不同所以不能比较。
func main() {
value2 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch value2[4] {
case 0, 1:
fmt.Println("0 or 1")
case 2, 3:
fmt.Println("2 or 3")
case 4, 5, 6:
fmt.Println("4 or 5 or 6")
}
}
上述代码可以通过编译,case中的数值类型转化为int8
每个case之间常量值不允许相同
func main() {
value3 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch value3[4] {
case 0, 1, 2:
fmt.Println("0 or 1 or 2")
case 2, 3, 4:
fmt.Println("2 or 3 or 4")
case 4, 5, 6:
fmt.Println("4 or 5 or 6")
}
}
error类型是一个接口类型
type error interface {
Error() string
}
package errors
// New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
error类型转字符串的时候调用Error()方法,而非String方法
某个函数中的某行代码有意或无意地引发了一个panic。这时,初始的panic详情会被建立起来,并且该程序的控制权会立即从此行代码转移至调用其所属函数的那行代码上,也就是调用栈中的上一级。控制权限一级级沿着调用栈反方向传播至顶端.由最顶端创建的所有routine都会终止.
func panic(v interface{})
困惑点:如何捕获新routine中的panic?
func main() {
defer func() {
if rec := recover(); rec != nil {
fmt.Println("panic,rec,", rec)
}
}()
fmt.Println("Enter function main.")
caller1()
fmt.Println("Exit function main.")
time.Sleep(100000000)
fmt.Println("sfs")
}
func caller1() {
fmt.Println("Enter function caller1.")
caller2()
fmt.Println("Exit function caller1.")
}
func caller2() {
time.Sleep(100000)
panic("new panic")
}
defer执行顺序与定义顺序完全相反
func main() {
defer func() {
fmt.Println("defer 1")
}()
defer func() {
fmt.Println("defer 2")
}()
defer func() {
fmt.Println("defer 3")
}()
fmt.Println("main...")
}