# 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
上次更新: 1/10/2023, 12:41:52 PM