函数
函数的定义
普通函数
函数定义有 func关键字,然后函数名,接着是传递的参数(是值传递,非引用传递),然后是返回类型。
func test(a,b int,c string)(int,string,bool){
return a + b,c,(a+b)>5
}
func main () {
a,b,c := test(5,2,"ccc")
fmt.Println(a,b,c)
}
//输出 7 ccc true
可变参数
return 没写返回值时,默认按返回参数格式去返回
func test(a,b int)(sum int,err error){
sum = a+b
return
}
func test(item ... int)(sum int,err error){
fmt.Println(item)
return
}
func main () {
aa := 2
a ,b:= test(1,aa)
fmt.Println(a,b)
}
输出
[1 2]
0
函数一等公民特性
可以当作变量来用,我们下面例子用 function这个变量 来赋值成test这个函数,然后用function来调用test函数。当变量来用时,赋值时不带括号,带括号等于使用了,此时变量变成接收返回值了。
func test(item ... int)(sum int,err error){
fmt.Println(item)
return
}
func main () {
function := test
aa := 2
a ,b:= function(1,aa)
fmt.Println(a,b)
返回函数,在cals这个里面返回一个函数。如果有多传参数也可以在返回的函数里面处理
func cals(op string) func() {
if op == "+" {
return func() {
fmt.Println("这是加法")
}
} else {
return func() {
fmt.Println("其他正在开发")
}
}
}
func main() {
cals("+")()
}
输出
这是加法
传入函数 匿名函数
func cals(one int ,myfunc func() int) int{
return one + myfunc()
}
func main () {
a :=1
b:=2
num := cals( 5,func ( )int{
return a + b
})
fmt.Println(num)
输出
8
//cals 传入2个参数,1个是one,我们传的5,另一个是我们自定义的func,func里面计算了a + b 也就是3。 所以就是 5 + 3(1+2) = 8
}
第二种方式 传入普通函数
func add(a , b int) (sum int){
return a+b
}
func cals(a int ,myfun c func(a int,b int) int) int{
return myfunc(a,5)
}
func main () {
fmt.Println(cals(3,add))
}
//输出 8
把上面的普通函数改成匿名函数
func cals(a int ,myfun c func(a int,b int) int) int{
return myfunc(a,5)
}
fmt.Println(cals(1,func(a , b int) int{
return a + b
}))
匿名函数传入变量在传进来也可以
func cals(a int ,myfunc func(a int,b int) int) int{
return myfunc(a,5)
}
func main () {
addfunc := func(a , b int) int{
return a + b
}
fmt.Println(cals(1,addfunc))
}
闭包的特性
闭包函数可以访问上一层的数据。 例如aotoIncrement 里面的闭包是可以访问到inc的。
这个的意思是我们 x := autoIncrement() x获取到aoto的实力并初始化了inc的变量。
然后返回的是匿名函数,调用一次匿名函数把auto的 inc + 1。但不会从新初始化inc = 0,除非我们再次调用,例如i ==3时我们再次调用。
func autoIncrement() func() int {
inc := 0;
return func() int{
inc +=1
return inc
}
}
func main () {
x := autoIncrement()
for i :=0;i<10;i++{
fmt.Println(x())
if i ==3{
x = autoIncrement()
}
}
}
输出
1
2
3
4
1
2
3
4
5
6
defer的应用场景
程序有些东西打开后,忘记关掉会有问题。但是打开又不能马上关可以用这个。。
例如我们要写文件或mysql链接时流程应该是,打开文件(获取锁),写数据,关闭文件(释放锁)
但是如果我们写数据逻辑如果N长,如果100行,或者会忘了。
我们可以用defer,函数执行完完回调(在return前执行)。栈方式执行
defer fmt.Println("1")
defer fmt.Println("2")
fmt.Println(3)
//输出
3
2
1
以上面例子,输出3,然后defer后进先出的栈原理,输出2,在1。
func defret() (ret int ){
defer func (){
ret +=5
}()
return
}
func main () {
fmt.Println(defret())
}
//输出 5
上面例子输出5,因为调用defret时,遇到return,执行时他执行defer调用匿名函数修改ret
error设计理念
其他语言都是try catch,go不行,要求我们必须处理error,所以代码会出现大量 if err != nil
这属于防御性编程,这种代码健壮性好,但写代码烦人。。
import "errors"
func A()(int,error){
return 1,errors.New("出错了")
}
func main () {
if _,err:= A(); err!= nil{
fmt.Println(err)
}
}
//第一个返回值我们用_接收掉了,可以不打印,第二个值我们打印err的数据
使用panic&panic
panic 会使程序退出,但会导致程序退出,不推荐用。。一般如果需要调用某些依赖时,才会用到这个抛出。
比如 调用某些依赖,mysql是否能连通,或者配置文件是否正确等才会用这个去抛出。。
这个也会打印一些详细信息
package main
import "fmt"
import "errors"
func A()(int,error){
panic("xxxd")
return 1,errors.New("出错了")
}
func main () {
if _,err:= A(); err!= nil{
fmt.Println(err)
}
}
//输出
panic: xxxd
goroutine 1 [running]:
main.A(...)
/data/168122338030043882.go:6
main.main()
/data/168122338030043882.go:10 +0x39
exit status 2
由于我们非主动调用panic,例如我们创建一个map没给初始化就去用它,也会包panic的错误。。这时候我们就用recover,这个函数能捕获到panic
package main
import "fmt"
import "errors"
func A()(int,error){
defer func (){
if r := recover() ;r != nil{
fmt.Println("打印错误信息了",r)
}
}()
var names map[string]string
names["xx"] = "xx"
return 1,errors.New("出错了")
}
func main () {
if _,err:= A(); err!= nil{
fmt.Println(err)
}
}
//输出
打印错误信息了 assignment to entry in nil map
当然我们的 panic(“xxxd”),也能被recover捕获到
defer要放在panic前面,不然就捕获不到了。
recover只有在defer的函数种调用才会生效。
recover处理异常后逻辑并不会回复到panic的那个点去。会被直接return
多个defer会形成栈,后入先出