- 小内存数据类型适用:非指针+返回值 !【传递速度更快,这也就是
math
中函数更喜欢非指针参数】 - 大内存数据类型适用:指针!【结构体】
声明函数
func 函数名(参数列表) (返回参数列表){
...
}
参数列表: 相同类型相邻参数,可以只保留最后一个类型:
func add(a,b int) int{
return a+b
}
返回参数列表: 【要么全使用非命名,要么全使用命名!】
func RetVlaues() (int ,int){
return 1,2
}
或可以命名后直接在函数中使用:
func RetVlaues() (a,b int){
a = 1
b = 2
return
}
调用函数
返回值变量列表 := 函数名(参数列表)
参数传递效果
- 数组等基本数据类型,直接会拷贝一份数据传入!如果需要原地修改,则使用
&变量名
传入数据地址让其进行操作! - 派生数据类型则本身会拷贝地址集合传入,让函数进行原地修改!但如果地址集合会被函数修改,则需要函数返回新的地址集合进行刷新! (大多数都需要return地址集合)
函数变量:把函数作为值保存到变量中
f
被成为回调函数!!!
var f func() //回调函数
var ff func() bool
func main(){
f = f1
f() //执行函数
ff = f2
f2() //执行函数
}
func f1() {
fmt.Println("f1被执行")
}
func f2() bool {
fmt.Println("f2被执行")
return true
}
用途:链式处理:其实就是将对数据的操作函数放入一个切片集合中,for range
切片执行每个函数!
var chain []func(T) T
chain中添加func!
for _,proc := chain {
result = proc(result)
}
匿名函数
没有名字的函数,常用于实现回调函数、闭包【访问外部变量的匿名函数!!!】等!
声明
func(参数列表) (返回参数列表){
...
}
实例
- 函数赋给变量,使用变量调用:
var f = func() bool{
return true
}
f()
- 直接构建调用:【后面的
(100)
就是函数调用并传入参数啊!】
func(a int) {
fmt.Println(a)
}(100)
函数类型实现接口:把函数作为接口来调用 (😵)
使用情况:
一、前提:有一个接口,只有一个方法:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
二、目的:想实现这个接口的这个函数,成为接口的实现体!但是,又不想每次实现一个都需要:一个新struct 、然后该struct是实现该接口(函数)!
三、方法:将目标函数ServeHTTP
创建成一种数据类型HandlerFunc
,该数据类型实现目标接口的方法!
这里的type和var是两种概念
- var HandlerFunc func(ResponseWriter,*Request):是声明了一个func(ResponseWriter,*Request)类型的函数
变量HandlerFunc
- type HandlerFunc func(ResponseWriter, *Request):是声明了一种func(ResponseWriter, *Request)的
数据类型HandlerFunc
(eg:type myInt int64)
type HandlerFunc func(ResponseWriter, *Request) /这是一种数据类型(类别成struct)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { /这是这种数据类型所拥有的函数(类比成struct的方法)
f(w, r)
}
四、实现:这样就可以发生一个神奇的事情:HandlerFunc(一个函数)
就可以返回一个Handler接口类型的实例,且传入的函数就是我们实现接口的函数!
// dest(w ResponseWriter, r *Request)是我们想要实现的ServeHTTP(ResponseWriter, *Request)‘
var s = HandlerFunc(dest) /将dest转换为HandlerFunc(哈哈哈没想到吧!)
那么s就是我们需要每次都构建的struct了
因为s现在是HandlerFunc类型,并且他实现了Handler
思想:任何type可以实现interface!!!
闭包(Closure):引用了外部变量的匿名函数
a := 0
闭包 := func(b int) int {
a++
return a+b
}
闭包(使用外部变量的匿名函数,不需要参数传进来),并不会拷贝外部变量,会直接修改外部变量的值! 所以它才表现出记忆能力!
可变参数
可变参数必须在参数列表中的最后:【可变参数在函数中被看作一个切片使用!】
func 函数名(固定参数列表,v... T)(返回参数列表){
...
}
判断参数类型
switch v.(type)
func TypeValue(v interface{}) {
switch v.(type) {
case int:
fmt.Println("类型为:int")
case bool:
fmt.Println("类型为:bool")
...
}
}
延迟执行语句defer
- 多个
defer
语句会放入延迟调用栈中 - return 语句后开始从栈中pop执行语句
- 目的: 释放锁、释放文件句柄
Go中的错误error
error
为一个错误接口
type error interface {
Error() string
}
errors
创建error
var err = errors.New("发生错误")
自定义error
func main() {
err = NewMyError(404,"找不到")
if err != nil {
log.Fatal(err)
}
}
// 1.声明错误结构
type myError struct {
code int
message string
}
// 2.实现error接口方法,返回错误描述
func (e *myError) Error() string{
return fmt.Sprintf("code:%d message:%s",e.code,e.message)
}
// 3.NewMyError 构造自定义错误并返回
func NewMyError(code int, message string) error {
return &myError{code: code,message: message}
}
宕机(panic):程序终止运行
- 直接调用内建函数:(注意可以传入任何参数)
func panic(v interface{})
- 由程序Runtime层抛出的panic崩溃
宕机恢复(recover):防止程序崩溃
如果panic
类似于抛出异常,那么recover
就相当于try catch
panic()
和recover()
模板:
(1)recover()
必须在defer
中!
(2)panic()
不会调用本函数中的defer
,所以panic()
和recover()
必须在两个函数中运行,当然panic()
先运行!
// recover函数
func ProtectFunc(entry func()) {
defer func() {
// 获取宕机异常
err := recover()
if err == nil {
return
}
switch err.(type) {
case runtime.Error:
fmt.Println("出现运行时宕机:",err)
default:
fmt.Println("手动宕机:",err)
}
}()
entry()
}
拓展
函数可以是一种变量,也可以是一种数据类型!
var HandlerFunc func(ResponseWriter,*Request) //是声明了一个func(ResponseWriter,*Request)类型的函数`变量HandlerFunc`
type HandlerFunc func(ResponseWriter, *Request) //是声明了一种func(ResponseWriter, *Request)的`数据类型HandlerFunc`(eg:type myInt int64)