# Day06 标准库errors与包管理
# 本节关键词
err是一个函数返回值
errors.New("自定义异常")
# 接口error
Go语言中把错误当成一种特殊的值来处理
# 接口error
type error interface {
Error() string
}
error
接口只包含一个方法Error
,返回一个描述错误信息的字符串- 当一个函数或方法需要返回错误时,我们通常把错误作为最后一个返回值,如
func Open(name string)(*File, error){
return OpenFile(name string, O_RDONLY, 0)
}
- 由于
error
是一个接口类型,默认零值为nil
,通常与nil
进行比较判断是否异常,如
file, err := Open("./file.text")
if err != nil {
fmt.Println("打开文件失败, err:", err)
return
}
fmt
打印err
时,自动调用Error
方法,也就是打印错误的描述信息
# errors.New 本质
- 源码开路
package errors
// New 构造函数,首字母大写,对外暴露
func New(text string) error {
return &errorString{text}
}
// errorString 结构体关注是什么,保存数据,首字母小写
type errorString struct {
s string
}
// 指针类型接收者,分析参数时类似self,(e *errorString)更加明确要处理的数据源
func (e *errorString) Error() string {
return e.s
}
// 定义error接口类型
type error interface {
Error() string
}
# 最佳实践
// 自定义error类型
var ErrInvalidOrderId = errors.New("无效订单ID")
var ErrServerConnection = errors.New("服务端连接异常")
type Order struct {
Id int64
Name string
}
func getOrderById(id int64) (*Order, error) {
if s := "服务端连接异常"; len(s) == 0 {
return nil, ErrServerConnection
}
if s := "无效订单ID"; len(s) > 0 {
return nil, ErrInvalidOrderId
}
return &Order{
Id: 1001,
Name: "Order1001",
}, nil
}
func main() {
order, err := getOrderById(1001)
// 1. 直接判断
if err == ErrServerConnection {
// 类似Sprintf,格式化动词%w,返回一个字符串
fmt.Println(fmt.Errorf("服务端连接异常: err%w", err))
return
}
// 2. errors.Is走起
if ok := errors.Is(err, ErrInvalidOrderId); ok {
fmt.Println("无效订单ID")
return
}
fmt.Println(order)
}
# 包 package
project
:项目 ---> 一个VsCode窗口 或者 一个GoLand窗口 打开一个项目(project)package
:包 --> 一个project 可以由多个 package 组成.go
文件:源码文件
# 定义包 package
package packagename
- 包名为
main
的包是应用程序的入口包,这种包编译后会得到一个可执行文件,编译不包含main包的源代码则不会得到可执行文件
# 标识符的可见性
- Go语言中是通过标识符的首字母大/小写来控制标识符的对外可见(public)/不可见(private)
package demo
import "fmt"
// 包级别标识符的可见性
// num 定义一个全局整型变量
// 首字母小写,对外不可见(只能在当前包内使用)
var num = 100
// Mode 定义一个常量
// 首字母大写,对外可见(可在其它包中使用)
const Mode = 1
// person 定义一个代表人的结构体
// 首字母小写,对外不可见(只能在当前包内使用)
type person struct {
name string
Age int
}
// Add 返回两个整数和的函数
// 首字母大写,对外可见(可在其它包中使用)
func Add(x, y int) int {
return x + y
}
// sayHi 打招呼的函数
// 首字母小写,对外不可见(只能在当前包内使用)
func sayHi() {
var myName = "七米" // 函数局部变量,只能在当前函数内使用
fmt.Println(myName)
}
标识符的首字母 什么时候大写,什么时候小写 都是有考量的!!!
# 包的引入
import importname "path/to/package"
- importname:引入的包名,通常都省略。默认值为引入包的包名
- path/to/package:引入包的路径名称,必须使用双引号包裹起来
import (
"fmt"
"net/http"
"os"
)
// alias 别名
import alias "fmt"
// 匿名引入
import _ "github.com/go-sql-driver/mysql"
- 匿名引入主要为了加载这个包,使得包资源得以初始化,
init
函数被执行一遍
# 初始化函数 init
// 无输入参数和返回值
func init(){
}
- 多包引入时,执行顺序如下
package main
import "fmt"
var x int8 = 10
const pi = 3.14
func init() {
fmt.Println("x:", x)
fmt.Println("pi:", pi)
sayHi()
}
func sayHi() {
fmt.Println("Hello World!")
}
func main() {
fmt.Println("你好,世界!")
}
- 先初始化变量,再默认依次执行 init 和 main
# 依赖包管理go module
Go源码的依赖包管理方案go mudule,1.16版本默认开启
# GOPROXY
go env -w GOPROXY=https://goproxy.cn,direct
# 下载依赖 go get
- 默认下载最新版本
go get github.com/ni-ning/achilles latest
- 下载指定版本
go get github.com/ni-ning/achilles@v1.0.0
- 默认下载到
$GOPATH/pkg/mod/
目录下 - 导入的路径名由被引入的包的 module 定义
go.mod
文件中记录了当前项目中所有依赖包的相关信息
module main
go 1.19
// require 声明依赖的关键字
// 依赖包的版本号
// latest:最新版本
// v1.0.0:详细版本号
// commit hash:指定某次commit hash
require github.com/ni-ning/achilles v1.0.0
- 直接引入即可
package main
import "github.com/ni-ning/achilles"
func main() {
achilles.SayHello()
}
- 不同于npm和pypi等,Go并没有提供一个中央仓库来管理所有依赖包,而是采用分布式的方式来管理包,为了防止依赖包被非法篡改,Go module 引入了go.sum机制来对依赖包进行校验。
# 发布依赖 module
- 见示例 https://github.com/ni-ning/achilles