Golang基础3-函数、nil相关

函数

    • 需要声明原型
    • 支持不定参数 func sum(numbers ...int)int
    • 支持返回多值
    • 支持递归
    • 支持命名返回参数
// 命名返回参数
func add(a, b int) (sum int) {sum = a + breturn // 这里不需要显式地写出返回值,因为已经在函数签名中声明了命名返回参数
}
    • 支持匿名函数、闭包
    • 函数也是一种类型,函数可以赋值给变量(本质函数指针)
    • 一个包中能有名字一样的函数
    • 不支持:重载(==,!=等等均不支持),默认参数

简单demo

package mainimport "fmt"//测试函数
func test(x, y int, s string) (int, string) {n := x + yreturn n, fmt.Sprintf("%s,%d\n", s, n)
}
func main() {a, b := test(1, 2, "你好")// _可以忽略某些值的返回fmt.Println(a)fmt.Println(b)}

回调函数demo

回调函数本质其实就是函数指针作为形参,传递给了函数,增加了代码的灵活度。

package mainimport "fmt"// 回调函数1
func testFunc(fn func() int) int {return fn()
}// 定义函数类型
type FormatFunc func(s string, x, y int) stringfunc format(fn FormatFunc, s string, x, y int) string {return fn(s, x, y)
}func formatHelper(s string, x, y int) string {return fmt.Sprintf(s, x, y)
}func main() {s1 := testFunc(func() int {return 100})fmt.Println(s1) //100//匿名函数,回调进行格式化返回string//s2 := format(func(s string, x, y int) string {//    return fmt.Sprintf(s, x, y)//}, "%d %d", 10, 20)s2 := format(formatHelper, "%d %d", 10, 20)fmt.Println(s2)
}

闭包demo

闭包很简单,可以理解为返回值是一个函数指针,其他的再看就很好理解了。

https://juejin.cn/post/6844903793771937805

package mainimport ("fmt"
)//返回函数指针 func()int
func a() func() int {i := 0b := func() int {i++fmt.Println(i)return i}return b
}func main() {//a执行完返回func() int 的这个函数指针,其中赋值给c,//那么这里面保存有这个b匿名函数的所有信息,实现了自增,可以不用定义全局变量c := a()c()c()c()//因为这个是函数指针a() //不会输出i
}

递归demo

package mainimport "fmt"// 递归1,求阶乘
func Factorial(n int) int {if n <= 1 {return 1}return n * Factorial(n-1)
}// 递归2,斐波那契数列
func Fibonaci(n int) int {if n == 0 {return 0}if n == 1 {return 1}return Fibonaci(n-1) + Fibonaci(n-2)
}
func main() {fmt.Println("5!=", Factorial(5))fmt.Println("前10项斐波那契数列:")for i := 0; i < 10; i++ {fmt.Printf("%d\t", Fibonaci(i))}
}

异常处理demo

参考文档:异常处理 · Go语言中文文档

使用panic抛出错误,defer捕获错误,一般panic中抛出异常,defer中捕获异常,之后正常处理。

panic:

内置函数,panic后的代码不执行, interface{},直到goroutine整个退出并报告错误,

recover:

1.利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。

2.recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。

3.多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。

painc处理demo

painc会导致程序直接退出,平时开发中尽量不随便使用。

一般场景:我的服务想要启动,必须依赖某些服务、日志、mysql能联通,配置文件没问题,那么才能启动的时候,直接使用panic

一旦服务启动,这时你的某行代码不小心触发panic,那么这就是重大事故(比如别人请求,你直接挂了)

但是架不住有些地方被动触发panic,这时就引入了recover来捕获panic

package mainimport "fmt"// painc部分后面代码不执行
func test() {defer func() {if err := recover(); err != nil {println("recover panic:", err.(string))}}()panic("panic错误测试!")//panic后的代码不执行//fmt.Println("panic后代码")
}func main() {test()fmt.Println("main")
}

error处理 demo
package mainimport ("errors""fmt"
)func A() (int, error) {return 2, errors.New("this is an error")}func main() {if _, err := A(); err != nil {fmt.Println(err)}
}

recover捕获panic的demo

recover需要延迟调用,也就是必须在defer的函数内部,否则返回nil

package mainimport "fmt"func except() {fmt.Println("except延迟函数调用!")fmt.Println("except延迟函数recover:", recover())
}func recoveryDemo() {//等效于下面的匿名延迟函数defer except()//延迟调用,recover在函数内部defer func() {if err := recover(); err != nil {fmt.Println("有效", err.(string))}}()//defer recover()              //无效,不是延迟调用,nildefer fmt.Println(recover()) //无效,空defer func() {func() {fmt.Println("defer inner")fmt.Println("defer inner", recover()) //无效}()}()panic("panic错误测试!")//不会执行fmt.Println("End of test!")
}func main() {recoveryDemo()fmt.Println("main")
}

总结:需要recover捕获panic时defer延迟函数进行接受,并且第一个有效的recover只能捕获最后一个painc(如果多个panic),之后有效的recover也返回nil。

defer的使用

defer延迟调用,一般释放资源和连接、关闭文件、释放锁等等。类似于java的finally和c++中析构函数,不过defer一般跟在函数或方法中。

参考博客:【Golang】Go语言defer用法大总结(含return返回机制)_golang defer return-CSDN博客

多个defer满足后进先出

defer跟无参、有参函数、方法
package mainimport "fmt"
//无返回值
func test(a int) {defer fmt.Println("defer1 = ", a)//方法defer func(v int) {fmt.Println("defer2 = ", v)}(a)//有参函数defer func() {fmt.Println("defer3 = ", a)}()//无参函数a += 100
}func main() {test(0)
}

defer满足后进先出,其次,有参情况下a会先传递进入,最后等a+=100之后执行完了再输出。

可读取函数返回值(return返回机制)

先return结构写入返回值,后defer收尾,最后携带返回值退出.

无名返回值,有名返回值的区别,见下面demo

无名有名返回值defer的demo

函数返回值可以无名、有名,这个是方便理解的不全代码,有名res的话本质局部变量,因此defer后会可能会影响res的返回值。而int这个返回值就直接定了。这个很容易引起bug,因此看下面例子:

package mainimport "fmt"// 无名返回值
func UnNamed(n int) int {n += 100defer func() {n += 100fmt.Println("UnNamed defer1", n)}()return n
}// 有名返回值
func Named(n int) (res int) {res = n + 100defer func() {res += 100fmt.Println("UnNamed defer1", res)}()// 返回res局部变量,因此受defer中的res的逻辑影响return res
}func main() {n := 0fmt.Println("main UnNamed return:", UnNamed(n))fmt.Println("main Named return:", Named(n))
}

对于第一无名返回值,先执行return保存返回值100,之后defer输出200,最后返回到main函数为100.

第二有名返回值,先执行return知道返回的是res(此时100),之后defer修改输出res200,最终返回到main为200.

同理可以更复杂,defer可以传入形参的无名有名函数,可以进行分析。

package mainimport "fmt"// 无名返回值
func UnNamed(n int) int {n += 100defer func(n int) { //传入100,输出110n += 10fmt.Println("UnNamed defer2", n)}(n)defer func() { //200n += 100fmt.Println("UnNamed defer1", n)}()return n //100
}// 有名返回值
func Named(n int) (res int) {res = n + 100 //100defer func(res int) { //传入100并且注意是值拷贝,并且入栈,110res += 10fmt.Println("UnNamed defer2", res)}(res)defer func() { //入栈res += 100fmt.Println("UnNamed defer1", res)}()return res //100->200
}func main() {n := 0fmt.Println("main UnNamed return:", UnNamed(n)) //100fmt.Println()fmt.Println("main Named return:", Named(n)) //200
}

因此传入指针等等,defer函数,有无名返回值均会影响main函数中接收到的最终return的值,请注意。

    • 调用os.Exit时defer不会被执行defer
    • 与panic进行配合处理异常

nil相关

nil代表某些数据类型的零值

不同类型0值

bool false

number 0

string ""

slice、map、channel、pointer、interface{} nil

如果是结构体,那么它的零值是,内部所有属性的零值的集合

nil 和empty的区别

这里分析了slice、map中的nil和empty的区别。

package mainimport "fmt"type Student struct {name stringage  int
}func main() {// nil slice,其实可以创建,append对nil slice进行了处理,但是map就不行var s1 []Student//s1 = append(s1, Student{"Bob", 19})fmt.Println(s1)if s1 == nil {fmt.Println("s1==nil")}// 不是nil slice,其实本质上是创建了,内部ptr指向一个空间为0的数组var s2 = make([]Student, 0)if s2 == nil {fmt.Println("s2==nil")}// nil mapvar m1 map[int]Studentif m1 == nil {fmt.Println("m1==nil")}//可以查询,但是无法添加键值对,panic(assignment to entry in nil map)//m1[1] = Student{"hhh", 123}//if val, ok := m1[1]; ok {//    fmt.Println("ok", val)//} else {//    fmt.Println("not ok")//}fmt.Println(m1)//不是nil map,已经初始化了,0个空间的mapvar m2 = make(map[int]Student, 0)//可以查询,插入数据了m2[1] = Student{"hhh", 123}if val, ok := m2[1]; ok {fmt.Println("ok", val)} else {fmt.Println("not ok")}fmt.Println(m2)}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/643653.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单片机学习过程

继电器光耦隔离电压转换步进电机直流电机 arduino是最好用的一种&#xff0c;他提供了完整的设备库文件&#xff0c;任何外部设备只要查找相应的库&#xff0c;就可以很方便的使用 &#xff0c; 但是如果不去学习51 或stm32 或 嵌入式玩玩还可以&#xff0c;如果碰到没有实现的…

单片机使用循环来实现延时和定时器延时的区别是什么?

循环延时是一种简单的实现方式&#xff0c;但由于资源占用和精确度的限制。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在…

用Excel做一个功能完备的仓库管理系统

1 基本设计思路 用到的Excel技术&#xff1a;sumif, vlookup, 表格(table)。基本思路&#xff1a;在有基础的商品、仓库等信息的情况下&#xff0c;对商品的每一个操作都有对应的单据&#xff0c;然后再汇总统计。标识&#xff1a;为了在不同的维度统计数量&#xff0c;各单据…

Spring与SpringBoot在配置读取方式上的区别

1. 问题说明 将Springboot项目中自定义的一个扩展工具类移植到Spring框架项目中的时候发现一个问题。在springboot中application.yml中的配置内容可以从Environment中获取&#xff0c;但是在spring中context:placeholder对应的配置文件中的配置却无法从Environment中获取。为了…

第67天:APP攻防-Frida反证书抓包移动安全系统资产提取评估扫描

思维导图 案例一&#xff1a;内在-资产提取-AppinfoScanne AppinfoScanner 一款适用于以 HW 行动/红队/渗透测试团队为场景的移动端(Android、iOS、WEB、H5、静态网站)信息收集扫描工具&#xff0c;可以帮助渗透测试工程师、攻击队成员、红队成员快速收集到移动端或者静态 WEB …

文字转粤语语音怎么转?文字转语音

文字转粤语语音怎么转&#xff1f;文字转粤语语音的应用&#xff0c;不仅展现了现代科技的魅力&#xff0c;也为我们提供了更加便捷的交流方式。它们将文字转化为粤语发音&#xff0c;让我们能够更直观地感受粤语的韵味和魅力。同时&#xff0c;这些软件还具备高度的可定制性&a…

TiDB 6.x 新特性解读 | Collation 规则

对数据库而言&#xff0c;合适的字符集和 collation 规则能够大大提升使用者运维和分析的效率。TiDB 从 v4.0 开始支持新 collation 规则&#xff0c;并于 TiDB 6.0 版本进行了更新。本文将深入解读 Collation 规则在 TiDB 6.0 中的变更和应用。 引 这里的“引”&#xff0c;…

Java:开发环境配置Windows/Liunx

Windows环境下JAVA_JDK安装及环境变量配置 &#xff08;1&#xff09;下载JDK&#xff0c;Java Downloads | Oracle &#xff08;2&#xff09;根据电脑的系统选择64位的版本进行下载 &#xff08;3&#xff09;下载完成&#xff0c;直接双击安装&#xff0c;所有选项直接默认…

Redis(四) 主从、哨兵、集群环境搭建

结合前三期 Redis(一) Redis简介(Redis(一) Redis简介-CSDN博客) Redis(二) 可编程性(Redis(二) 可编程性-CSDN博客) Redis(三) 事务与发布订阅(Redis(三) 事务与发布订阅-CSDN博客) 目录 1.0 Redis主从 1.1 Redis 主从结构的基本原理和工作方式 1.2 Redis 主从结构的好处 …

数据结构(Wrong Question)

一、绪论 1.1 数据结构的基本概念 D 因为抽象数据类型&#xff08;ADT&#xff09;描述了数据的逻辑结构和抽象运算&#xff0c;通常用&#xff08;数据对象&#xff0c;数据对象&#xff0c;基本操作集&#xff09;这样的三元组来表示&#xff0c;从而可构成一个完整的数据结…

Redis篇:缓存击穿及解决方案

1.何为缓存击穿 缓存击穿问题也叫热点Key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了&#xff08;有可能是正好过期了&#xff09;&#xff0c;无数的请求访问会在瞬间给数据库带来巨大的冲击。 常见的解决方案有两种&#xff1a; 互斥锁 逻…

【Linux】深入理解Linux文件系统与日志分析

目录 一、inode与block 1.block与inode概述 2.inode的内容 3.inode号码 4.inode的大小 5.访问文件的简单流程 6.inode的特殊作用 7.通过indoe号删除rm常规方法删除不掉的文件 二、硬链接和软链接 三、恢复误删除的文件 1.恢复EXT类型的文件 示例 2.xfs类型文件备份…