Go语言学习笔记(五)

devtools/2025/3/4 1:25:40/

文章目录

  • 十八、go操作MySQL、Redis
    • MySQL
    • Redis
  • 十九、泛型
    • 泛型函数
    • 泛型类型
    • 泛型约束
    • 泛型特化
    • 泛型接口
  • 二十、workspaces
    • 核心概念
    • 示例
  • 二十一、模糊测试

十八、go操作MySQL、Redis

MySQL

package mainimport ("database/sql""errors""fmt"_ "github.com/go-sql-driver/mysql""log""time"
)type User struct {UserId   int    `db:"id"`Username string `db:"name"`Phone    string `db:"phone"`Email    string `db:"email"`IdCard   string `db:"id_card"`
}var DB *sql.DBfunc init() {db, err := sql.Open("mysql", "root:123456@tcp(localhost:3306)/springbootclass")if err != nil {panic(err)}//最大空闲连接数,默认不配置,是2个最大空闲连接db.SetMaxIdleConns(5)//最大连接数,默认不配置,是不限制最大连接数db.SetMaxOpenConns(100)// 连接最大存活时间db.SetConnMaxLifetime(time.Minute * 3)//空闲连接最大存活时间db.SetConnMaxIdleTime(time.Minute * 1)err = db.Ping()if err != nil {log.Println("数据库连接失败")db.Close()panic(err)}DB = db}
func query(id int) (*User, error) {rows, err := DB.Query("select * from user where id=? limit 1", id)if err != nil {log.Println("执行sql语句出错")panic(err)}user := new(User)for rows.Next() {if err := rows.Scan(&user.UserId, &user.Username, &user.Phone, &user.Email, &user.IdCard); err != nil {log.Println("scan error:", err)return nil, errors.New(err.Error())}}return user, nil}
func save() {r, err := DB.Exec("insert into user (name,phone,email) values(?,?,?)", "test001", "man", "001@test.com")if err != nil {log.Println("执行sql语句出错")panic(err)}id, err := r.LastInsertId()if err != nil {log.Println("数据库无法连接")panic(err)}fmt.Println("插入成功:", id)
}func update(username string, id int) {ret, err := DB.Exec("update user set username=? where user_id=?", username, id)if err != nil {log.Println("更新出现问题:", err)return}affected, _ := ret.RowsAffected()fmt.Println("更新成功的行数:", affected)
}
func delete(id int) {ret, err := DB.Exec("delete from user where user_id=?", id)if err != nil {log.Println("删除出现问题:", err)return}affected, _ := ret.RowsAffected()fmt.Println("删除成功的行数:", affected)
}
func insertTx(username string) {tx, err := DB.Begin()if err != nil {log.Println("开启事务错误:", err)return}ret, err := tx.Exec("insert into user (username,sex,email) values (?,?,?)", username, "man", "test@test.com")if err != nil {log.Println("事务sql执行出错:", err)return}id, _ := ret.LastInsertId()fmt.Println("插入成功:", id)if username == "lisi" {fmt.Println("回滚...")_ = tx.Rollback()} else {_ = tx.Commit()}}
func main() {defer DB.Close()//save()query(14)
}

Redis

安装:go get github.com/go-redis/redis/v8

package mainimport ("context""fmt""github.com/go-redis/redis/v8"
)func main()  {ctx := context.Background()rdb := redis.NewClient(&redis.Options{Addr:	  "localhost:6379",Password: "", // no password setDB:		  0,  // use default DB})err := rdb.Set(ctx, "key", "value", 0).Err()if err != nil {panic(err)}val, err := rdb.Get(ctx, "key").Result()if err != nil {panic(err)}fmt.Println("key", val)val2, err := rdb.Get(ctx, "key2").Result()if err == redis.Nil {fmt.Println("key2 does not exist")} else if err != nil {panic(err)} else {fmt.Println("key2", val2)}
}

十九、泛型

泛型(Generics)是一种编程思想,它允许在编写代码时使用未知的类型。泛型可以增加代码的灵活性和可复用性,同时还能提高代码的安全性和可读性。泛型在 C++, Java 和 Python 等语言中已经被广泛应用,但在 Go 中一直未被支持。

Go 1.18 版本终于加入了泛型特性,这一特性的引入被认为是 Go 语言历史上的一件大事。

详细文档:Tutorial: Getting started with generics - The Go Programming Language

示例:

package mainimport "fmt"// // SumInts adds together the values of m.
//
//	func SumInts(m map[string]int64) int64 {
//		var s int64
//		for _, v := range m {
//			s += v
//		}
//		return s
//	}
//
// // SumFloats adds together the values of m.
//
//	func SumFloats(m map[string]float64) float64 {
//		var s float64
//		for _, v := range m {
//			s += v
//		}
//		return s
//	}
//
// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {var s Vfor _, v := range m {s += v}return s
}
func main() {// Initialize a map for the integer valuesints := map[string]int64{"first":  34,"second": 12,}// Initialize a map for the float valuesfloats := map[string]float64{"first":  35.98,"second": 26.99,}fmt.Printf("Non-Generic Sums: %v and %v\n",//SumInts(ints),//SumFloats(floats))SumIntsOrFloats(ints),SumIntsOrFloats(floats))
}

泛型函数

在 Golang 中,泛型的语法包括类型参数、类型约束、泛型函数和泛型类型等。

在 Go 中,泛型函数的语法如下:

 func FuncName[T Type](params) returnType {// Function body}

其中,T 表示泛型类型参数,Type 表示具体的类型,params 表示函数的参数,returnType 表示函数的返回值类型。

泛型类型

除了泛型函数之外,Go 1.18 版本还引入了泛型类型。泛型类型的语法如下:

 type TypeName[T Type] struct {// Fields}

其中,TypeName 表示泛型类型名称,T 表示泛型类型参数,Type 表示具体的类型。

泛型约束

在使用泛型时,有时需要对泛型类型进行一定的约束。例如,我们希望某个泛型函数或类型只能接受特定类型的参数,或者特定类型的参数必须实现某个接口。在 Go 中,可以使用泛型约束来实现这些需求

类型约束可以让泛型函数或类型只接受特定类型的参数。在 Go 中,类型约束可以使用 interface{} 类型和类型断言来实现。

类型约束可以使用在类型参数后加上一个约束类型来实现。

除了使用 interface{} 类型进行类型约束之外,Go 还支持使用接口来约束泛型类型。

泛型特化

泛型特化是指将泛型代码转换为具体类型的代码。在 Go 中,泛型特化是在编译期间完成的。特化可以提高代码的性能和运行效率,因为编译器可以针对具体类型进行优化,避免了运行时的类型检查和类型转换。

在 Go 中,泛型特化是通过代码生成器实现的。代码生成器会根据泛型类型或函数的定义,生成具体类型或函数的代码。例如,下面是一个泛型函数的定义:

css 代码解读复制代码 func Swap[T any](a, b *T) {*a, *b = *b, *a}

该函数可以交换任意类型的两个变量的值。在编译期间,代码生成器会根据调用该函数时传递的参数类型生成具体的函数代码。例如,如果传递的是整数类型的指针,代码生成器会生成以下代码:

css 代码解读复制代码 func Swap_int(a, b *int) {*a, *b = *b, *a}

如果传递的是字符串类型的指针,代码生成器会生成以下代码:

css 代码解读复制代码 func Swap_string(a, b *string) {*a, *b = *b, *a}

泛型接口

泛型接口是一种可以处理多种类型数据的接口。在 Golang 中,可以使用类型参数来实现泛型接口。

二十、workspaces

Go 多模块工作区能够使开发者能够更容易地同时处理多个模块的工作,如:

方便进行依赖的代码调试(打断点、修改代码)、排查依赖代码 bug
方便同时进行多个仓库/模块并行开发调试

Tutorial: Getting started with multi-module workspaces - The Go Programming Language

go 使用的是多模块工作区,可以让开发者更容易同时处理多个模块的开发。在 Go 1.17 之前,只能使用 go.mod replace 指令来实现,如果你正巧是同时进行多个模块的开发,使用它可能是很痛苦的。每次当你想要提交代码的时候,都不得不删除掉 go.mod 中的 replace 才能使模块稳定的发布版本。•在使用 go 1.18 多模块工作区功能的时候,就使用这项工作变得简单容易处理。下面我来介绍怎么使用这一功能。•Go 多模块工作区文档、代码示例[5]

核心概念

在 Go 中,工作区(workspace)是由一个包含多个 Go 模块的目录结构构成的,主要用于管理依赖关系、构建过程以及代码模块的交互。

一个 Go 工作区通常由以下部分组成:

  • 多个模块:每个模块都有自己的 go.mod 文件,定义了该模块的依赖、版本等。
  • 共享的 go.work 文件:在 Go 1.18 引入的 go.work 文件中,指定了多个 Go 模块的位置,Go 工具链通过这个文件了解工作区的所有模块,并能处理模块间的依赖。

go.work 文件是工作区的核心,类似于 go.mod 文件,但它不是用来定义单个模块的依赖,而是用来定义多个模块如何协作、如何管理工作区中的依赖关系。通过这个文件,Go 工具链知道工作区内所有模块的位置,并且能够更好地进行依赖解析和构建。

go.work 文件的基本结构

goCopyEditgo 1.18use (./module1./module2
)
  • go 1.18:指定 Go 的版本。
  • use:指定工作区中的模块位置。每个模块路径指向一个相对路径或 Git 仓库。

示例

创建工作区

  1. 创建一个新的目录作为工作区。
  2. 在工作区中初始化多个 Go 模块(每个模块都有独立的 go.mod)。
  3. 创建 go.work 文件并将模块添加到该文件中。

示例

  1. 创建一个工作区目录 myworkspace,并在其中创建两个模块:

    mkdir myworkspace
    cd myworkspace
    go mod init module1
    cd ..
    go mod init module2
    
  2. 创建 go.work 文件来定义工作区:

    go work init ./module1 ./module2
    

    这样就创建了一个包含 module1module2 的工作区,go.work 文件会自动更新,指示 Go 工具链知道这些模块是工作区的一部分。

二十一、模糊测试

Go 1.18在go工具链里引入了fuzzing模糊测试,可以帮助我们发现Go代码里的漏洞或者可能导致程序崩溃的输入。

参考文章:Tutorial: Getting started with fuzzing - The Go Programming Language

package mainimport "fmt"func Reverse(s string) string {fmt.Printf("input: %q\n", s)r := []rune(s)fmt.Printf("runes: %q\n", r)for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {r[i], r[j] = r[j], r[i]}return string(r)
}
func main() {input := "The quick brown fox jumped over the lazy dog"rev := Reverse(input)doubleRev := Reverse(rev)fmt.Printf("original: %q\n", input)fmt.Printf("reversed: %q\n", rev)fmt.Printf("reversed again: %q\n", doubleRev)
}

fuzzing的优点之一是可以基于开发者代码里指定的测试输入作为基础数据,进一步自动生成新的随机测试数据,用来发现指定测试输入没有覆盖到的边界情况。

在单元测试里,因为测试输入是固定的,你可以知道调用Reverse函数后每个输入字符串得到的反转字符串应该是什么,然后在单元测试的代码里判断Reverse的执行结果是否和预期相符。例如,对于测试用例Reverse("Hello, world"),单元测试预期的结果是 "dlrow ,olleH"

但是使用fuzzing时,我们没办法预期输出结果是什么,因为测试的输入除了我们代码里指定的用例之外,还有fuzzing随机生成的。对于随机生成的测试输入,我们当然没办法提前知道输出结果是什么。

虽然如此,本文里的Reverse函数有几个特性我们还是可以在模糊测试里做验证。

  1. 对一个字符串做2次反转,得到的结果和源字符串相同
  2. 反转后的字符串也仍然是一个有效的UTF-8编码的字符串

注意:fuzzing模糊测试和Go已有的单元测试以及性能测试框架是互为补充的,并不是替代关系。

参考文章:官方教程:Go fuzzing模糊测试 - 知乎 (zhihu.com)


http://www.ppmy.cn/devtools/164331.html

相关文章

考虑复杂遭遇场景下的COLREG,基于模型预测人工势场的船舶运动规划方法附Matlab代码

考虑复杂遭遇场景下的COLREG&#xff0c;基于模型预测人工势场的船舶运动规划方法附Matlab代码 一、引言 1.1、研究背景和意义 随着全球航运业的迅猛发展&#xff0c;船舶交通密度不断增大&#xff0c;海上交通事故频发&#xff0c;严重威胁到海上航行的安全。国际海上避碰规…

HTTP 协议的发展历程:从 HTTP/1.0 到 HTTP/2.0

HTTP 协议的发展历程&#xff1a;从 HTTP/1.0 到 HTTP/2.0 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是 Web 的基础协议&#xff0c;用于客户端和服务器之间的通信。从 HTTP/1.0 到 HTTP/2.0&#xff0c;HTTP 协议经历了多次重大改…

Python 爬虫实战案例 - 获取拉勾网招聘职位信息

引言 拉勾网&#xff0c;作为互联网招聘领域的佼佼者&#xff0c;汇聚了海量且多样的职位招聘信息。这些信息涵盖了从新兴科技领域到传统行业转型所需的各类岗位&#xff0c;无论是初出茅庐的应届生&#xff0c;还是经验丰富的职场老手&#xff0c;都能在其中探寻到机遇。 对…

Android 12 AOSP拦截Home键教程

在 Android 12 的 AOSP&#xff08;Android Open Source Project&#xff09;中&#xff0c;拦截 Home 键的返回操作需要修改系统级别的代码。由于 Home 键是系统级别的按键&#xff0c;通常由系统处理&#xff0c;因此拦截它需要深入系统框架层进行修改。 以下是一个大致的步…

UE5切换关卡函数OpenLevel,输入模式结构体,UI界面

1.输入模式结构体 FInputModeGameOnly&#xff1a;玩家只能与游戏世界交互&#xff0c;UI 不可交互。FInputModeGameAndUI&#xff1a;玩家可以与游戏世界和 UI 同时交互。FInputModeUIOnly&#xff1a;玩家只能与 UI 交互&#xff0c;无法与游戏世界进行互动。 FInputModeGam…

Ubuntu20.04安装Redis

目录 切换到root用户 使用 apt install redis 安装redis 修改配置文件 ​编辑 重新启动服务器 使用Redis客户端连接服务器 切换到root用户 如果没有切换到root用户的&#xff0c;切换到root用户。 使用 apt install redis 安装redis 遇到y/n直接y即可。 redis安装好之…

PyG结合MP api 实现深度学习对材料性能预测的简单案例分析

使用 PyTorch Geometric (PyG) 和 Material Project API (MP API)&#xff0c;结合深度学习技术预测材料的性能。选择预测材料的带隙&#xff08;band gap&#xff09; 作为任务&#xff0c;带隙是材料的一个关键性能&#xff0c;影响其导电性和光学性质。通过这个案例&#xf…

leetcode第39题组合总和

原题出于leetcode第39题https://leetcode.cn/problems/combination-sum/description/题目如下&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以…