Golang基础3-函数、nil相关

函数

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

简单demo

package main

import "fmt"

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

}

回调函数demo

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

package main

import "fmt"

// 回调函数1
func testFunc(fn func() int) int {
    return fn()
}

// 定义函数类型
type FormatFunc func(s string, x, y int) string

func 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 main

import (
    "fmt"
)

//返回函数指针 func()int
func a() func() int {
    i := 0
    b := 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 main

import "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 main

import "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 main

import (
    "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 main

import "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()              //无效,不是延迟调用,nil
    defer 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 main

import "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 main

import "fmt"

// 无名返回值
func UnNamed(n int) int {
    n += 100

    defer func() {
        n += 100
        fmt.Println("UnNamed defer1", n)
    }()

    return n
}

// 有名返回值
func Named(n int) (res int) {
    res = n + 100

    defer func() {
        res += 100
        fmt.Println("UnNamed defer1", res)
    }()
    // 返回res局部变量,因此受defer中的res的逻辑影响
    return res
}

func main() {
    n := 0
    fmt.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 main

import "fmt"

// 无名返回值
func UnNamed(n int) int {
    n += 100

    defer func(n int) { //传入100,输出110
        n += 10
        fmt.Println("UnNamed defer2", n)
    }(n)

    defer func() { //200
        n += 100
        fmt.Println("UnNamed defer1", n)
    }()

    return n //100
}

// 有名返回值
func Named(n int) (res int) {
    res = n + 100 //100

    defer func(res int) { //传入100并且注意是值拷贝,并且入栈,110
        res += 10
        fmt.Println("UnNamed defer2", res)
    }(res)

    defer func() { //入栈
        res += 100
        fmt.Println("UnNamed defer1", res)
    }()

    return res //100->200
}

func main() {
    n := 0
    fmt.Println("main UnNamed return:", UnNamed(n)) //100

    fmt.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 main

import "fmt"

type Student struct {
    name string
    age  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 map
    var m1 map[int]Student
    if 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个空间的map
    var 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)

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jackson 2.x 系列【30】Spring Boot 集成之数据脱敏

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 本系列Spring Boot 版本 3.2.4 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 概述2. 实现思路3. 案例演示3.1 脱敏规则3.2 自…

图像处理之Retinex算法(C++)

图像处理之Retinex算法&#xff08;C&#xff09; 文章目录 图像处理之Retinex算法&#xff08;C&#xff09;前言一、单尺度Retinex&#xff08;SSR&#xff09;1.原理2.代码实现3.结果展示 二、多尺度Retinex&#xff08;MSR&#xff09;1.原理2.代码实现3.结果展示 三、带色…

Linux加强篇-存储结构与管理硬盘(一)

目录 ⛳️推荐 从“/”开始 物理设备命名规则 文件系统与数据资料 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 从“/”开始 Linux系统中一切都是文件&#xff0c;都是从“…

deep learning

谷歌在线notebook 一、基本数据类型与用法 1.torch.tensor(张量) 按照维度不同(中括号的对数)&#xff0c;可以用torch.tensor创建scalar(标量)、vector(向量)、matrix(矩阵)&#xff0c; 一般的&#xff0c;一维是标量&#xff0c;二维是向量&#xff0c;三维是矩阵&#…

银河麒麟V10 SP1服务器客户端定时数据同步

银河麒麟V10 SP1服务器客户端定时数据同步 0.概述 当前只测试了将数据从客户端往服务端推送&#xff0c;两个客户端分别推送不同的数据 1.环境 三台电脑均为银河麒麟V10SP1桌面操作系统 服务器IP&#xff1a;192.168.1.51 用户名&#xff1a;wlh 客户端IP&#xff1a;192…

LabVIEW和MES系统的智能化车间数据对接

LabVIEW和MES系统的智能化车间数据对接 随着工业4.0时代的到来&#xff0c;智能制造成为推动制造业高质量发展的重要手段。其中&#xff0c;数字化车间作为智能制造的重要组成部分&#xff0c;其设计与实现至关重要。在数字化车间环境下&#xff0c;如何利用LabVIEW软件与MES系…

解析SoC芯片:构建智能设备的核心技术

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

linux磁盘原理

在linux系统中&#xff0c;对磁盘进行管理与windows系统类似&#xff0c;都要先分区&#xff0c;格式化&#xff0c;创建文件系统&#xff0c;挂载目录&#xff0c;数据写入

【PHP开发工程师详细讲解分析】——网站注册账号(头像的上传操作),让自己喜欢的头像更换畅通无阻

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

便携式应急指挥箱规格参数

概况: 微缩型的无线视频音频传输的机动挥所。体积小、重量轻、公配电方便、携带便携、功能齐全。可进行单兵作战&#xff0c;通过此无线音频视频传输的指挥箱能完成现场图像、语音、数据的采集等功能&#xff0c;可以通过5G/4G/WIFI等多种无线网络完成传输的需求&#xff0c;或…

计算机网络相关知识总结

一、概述 计算机网络可以极大扩展计算机系统的功能机器应用范围&#xff0c;提高可靠性&#xff0c;在为用户提供放方便的同时&#xff0c;减少了整体系统费用&#xff0c;提高性价比。 计算机网络的功能主要有&#xff1a;1. 数据共享&#xff1b;2. 资源共享&#xff1b;3. 管…

echart坑

echart坑 原因&#xff1a; 引用了echarts里面的init方法显示没有定义 解决的方法 将import echarts from echarts 的引入方式改为&#xff1a; import * as echarts from echarts

【vue2】实现微信截图(复制图片)在项目内可粘贴

需求 后台管理在上传图片地方需要将复制的图片粘贴上传 一、添加事件 在原有上传组件的基础上添加 paste事件 二、方法 onPaste(e) {const items (e.clipboardData || window.clipboardData).items;let blob null;for (let i 0; i < items.length; i) {if (items[i].ty…

学习Rust的第10天:枚举和模式匹配

今天我们来看看一个类似的概念 enums 。 Enums: We saw that in Rust, enums are data types that list possible values, giving a simple and type-safe mechanism to describe alternatives. We looked at how to create enums and use them to represent similar possibili…

Dropout Feature Ranking for Deep Learning Models

摘要 深度神经网络( deep neural networks&#xff0c;DNNs )在多个领域取得了最新的研究成果。不幸的是&#xff0c;DNNs因其不可解释性而臭名昭著&#xff0c;从而限制了其在生物和医疗保健等假说驱动领域的适用性。此外&#xff0c;在资源受限的环境下&#xff0c;设计依赖…

沐风老师3dMax万有引力插件ToGround使用方法详解

3dMax万有引力插件ToGround使用教程 3dMax万有引力插件ToGround&#xff0c;用于在复杂地形&#xff08;曲面&#xff09;上将对象放置在适当高度的实用工具。例如&#xff1a;将大量的人、植物和汽车快速放置在一个街道、公园和小跑道高度不同的区域尤其有用。 【适用版本】 …

android openGL ES详解

1、渲染线程与主线程的通信 两个线程之间的通信可以用如下方法: 在主线程中的 GLSurfaceView 实例可以调用 queueEvent( &#xff09;方法传递一个 Runnable 给后台渲染线程&#xff0c;渲染线程可以调用 Activity 的 runOnUIThread()来传递事件 (event) 给主线程。 2、顶点…

SQLite FTS3 和 FTS4 扩展(三十二)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite 的命令行 Shell(三十一&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 概述 FTS3 和 FTS4 是 SQLite 虚拟表模块&#xff0c;允许用户执行 对一组文档进行全文搜索。最常见&#xff08;和最有效…

Linux之yum和vim的使用

一、yum的使用 yum 后面跟install要安装的文件名&#xff1a; 若你要安装的文件已经存在&#xff0c;则会出现&#xff1a; 要删除文件&#xff1a; yum remore文件名即可删除 在我们安装完lrzsz之后&#xff0c;可以用rz指令和sz指令&#xff1a; rz指令可以从window窗口中…

开源模型应用落地-chatglm3-6b-集成langchain(十)

一、前言 langchain框架调用本地模型&#xff0c;使得用户可以直接提出问题或发送指令&#xff0c;而无需担心具体的步骤或流程。通过LangChain和chatglm3-6b模型的整合&#xff0c;可以更好地处理对话&#xff0c;提供更智能、更准确的响应&#xff0c;从而提高对话系统的性能…