Go红队开发—语法补充

news2025/3/1 5:45:05

文章目录

  • 错误控制
    • 使用
    • 自定义错误类型
    • 错误包装
    • errors.Is 和 errors.As
    • panic捕获、recover 、defer
    • 错误控制练习
  • 接口
    • 结构体实现接口
    • 基本类型实现接口
    • 切片实现接口
  • 接口练习
  • Embed嵌入文件

之前有师傅问这个系列好像跟红队没啥关系,前几期确实没啥关系,因为这都是进行红队工具开发的前置知识点,对于我个人强迫症而言只是想让这个系列更加完善而已,所以前置知识也加进去了,有GO知识的大佬可以等下一期哈!感谢支持。

错误控制

使用

1. errors.New("错误信息")   //这个属于error类型
例子://你会看到error返回类型,return 0, errors.New("除数不能为零") 
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为零") }
return a / b, nil
}

2.fmt.Errorf:错误包装,后面会详细讲。这个函数允许你使用格式化字符串创建错误,类似于 fmt.Sprintf。
这个没啥好说的,只是说New的时候只能是字符串,而你用这个就能够格式化字符串,将变量放在里面格式化输出在错误中。
err := fmt.Errorf("invalid argument: %v", value)

3.errors.Is:判断错误链中是否包含该错误
if errors.Is(err, myError) {
    // err 是 myError自定义
}

自定义错误类型

自定义错误类型:通过实现 error 接口来自定义错误类型。

示例代码:

package main

import "fmt"

type MyError struct {
	When string
	What string
}

func (e *MyError) Error() string {
	return fmt.Sprintf("在 %s 发生 %s ", e.When, e.What)
}
func run() error {
	return &MyError{
		When: "运行时",
		What: "错误1",
	}
}
func main() {
	err := run()
	if err != nil {
		fmt.Println(err)
	}
}

注意理解具体执行过程:

  1. run() 返回一个 error 接口值,其动态类型为 *MyError,动态值为 &MyError{}

  2. 当执行

    fmt.Println(err)
    

    时:

    • err.Error() 被隐式调用。
    • 动态类型 *MyErrorError() 方法被执行,返回一个字符串。

错误包装

其实很好理解,就是使用fmt.Errorf("获取数据失败: %w", err),这相当于用获取数据失败:这个字符串包了一下err

所以fmt.Errorf("获取数据失败: %w", err)解包的时候就是等于err,因为后面的Is(is)就是为啥能判断错误链里是否包含的。

package main

import (
	"errors"
	"fmt"
)

// 定义一个自定义错误
var ErrNotFound = errors.New("资源未找到")

func fetchData(id int) error {
	if id == 0 {
		return ErrNotFound // 返回原始错误
	}
	return nil
}

func main() {
	// 调用 fetchData 并包装错误
	err := fetchData(0)
	if err != nil {
		// 使用 fmt.Errorf 包装原始错误,添加上下文
		wrappedErr := fmt.Errorf("获取数据失败: %w", err)
		fmt.Println(wrappedErr) // 打印包装后的错误信息

		// 检查包装的错误是否包含特定错误
		if errors.Is(wrappedErr, ErrNotFound) {
			fmt.Println("错误类型: 资源未找到")
		}

		// 解包原始错误
		unwrappedErr := errors.Unwrap(wrappedErr)
		fmt.Printf("解包后的错误: %v\n", unwrappedErr)
	}
}

输出如下:(看下面的输出就知道什么情况了)

获取数据失败: 资源未找到
错误类型: 资源未找到
解包后的错误: 资源未找到

errors.Is 和 errors.As

了解了错误包装之后就知道这两的区别了,Is就是判断错误链中是否包含你这个错误,只要包含一个即可。

As就是只判断当前的,不管你是否包含的。

package main

import (
	"errors"
	"fmt"
)

var ErrDivideByZero = errors.New("除数不能为零")

func divide(a, b int) (int, error) {
	if b == 0 {
		return 0, ErrDivideByZero
	}
	return a / b, nil
}
func main() {
	_, err := divide(4, 0)
	if errors.Is(err, ErrDivideByZero) { //可以是ErrDivideByZero错误的错误链,因为有可能是进行了错误包装
		fmt.Println("捕获到除以零的错误")
	} else {
		fmt.Println("其他错误:", err)
	}
    
    if errors.As(err, &ErrDivideByZero) {  //一定要是ErrDivideByZero错误,同时要是一个指针,源码的painc写着target must be a non-nil pointer
		fmt.Println("捕获到除以零的错误")
	} else {
		fmt.Println("其他错误:", err)
	}
}

panic捕获、recover 、defer

解释:

  • panic 能够改变程序的控制流,调用 panic 后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方的 defer
  • recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用;

注意:recover 只能在 defer 函数中调⽤,并且只有在 panic 发⽣时才会返回⾮ nil 值。

//恐慌捕获 panic recover defer
func start_panic() {

	defer func() {
		//使用defer来等待后面panic执行一个panic后再进行捕获
		if r := recover(); r != nil {
			fmt.Println("捕获到了:", r)
		}
	}()
	panic("一个panic")
}
	
func main() {
	fmt.Println("开始捕获panic")
	start_panic()
	fmt.Println("结束捕获panic")
}

错误控制练习

固定打开一个1.txt文件,然后使用自定义错误类型,同时进行panic捕获,输出预期:错误,非预期:错误

type FileNotFoundError struct {
	filename string
}

func (f FileNotFoundError) Error() string {
	return fmt.Sprintf("文件 %s 不存在。", f.filename)
}

func readfile(file_name string) (string, error) {
	//panic一下
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("panic is ", r)
		}
	}()

	if file_name != "1.txt" {
		return "", FileNotFoundError{filename: file_name}
	}
	bytes, err := os.ReadFile("1.txt")
	if err != nil {
		return "", fmt.Errorf("文件 %s 打开出错", file_name)
	} else {
		return string(bytes), nil
	}
}

func main() {
	file, err := readfile("1.txt")
	if err != nil {
		if errors.As(err, FileNotFoundError{}) {
			fmt.Println("预期错误:", err)
		} else {
			fmt.Println("非预期错误:", err)
		}
		return
	}
	fmt.Println("文件内容为:", file)
}

接口

接口在go语言中也有点抽象,对于接口的实现其实很简单,只是在用的时候比较抽象,结构体实现接口反而是最容易接受的,像基本类型还有切片这两种接口的使用就比较抽象。

结构体实现接口

(在结构体实现结构以及方法的调用基本没啥问题,很正常的操作)

package main

import "fmt"

type User interface {
	getName() string
	getAge() int
}

type Person struct {
	name string
	age  int
}

func (p Person) getName() string {
	return p.name
}
func (p Person) getAge() int {
	return p.age
}

func main() {
	p := Person{
		name: "zhangsan",
		age:  18,
	}
	fmt.Println(p.getName(), p.getAge())
}

基本类型实现接口

这里我分两种情况,string和int实现接口,目的是了解在实现接口后怎么使用这个方法。

type Stringer interface {
//接口的这两个方法写了之后就要实现。所以下面就实现了
	String() string
	Ascii() string
}
type MyString string //string类型
type MyInt int       //int类型

func (s MyString) String() string {
	return string(s) //其实可以不转string也能直接返回,因为MyString本身就是string类型,只是我换了个别名而已
}
func (t MyInt) Ascii() string {
	return string(t) //一定要转,因为本身是int类型
}
func (t MyInt) String() string {
	return fmt.Sprintf("%d", t) //一定要转,因为本身是int类型
}

func main() {
	//结构体实现接口效果
	//start_struct_interface()

	var s Stringer = MyString("传递参数string")  
	var i Stringer = MyInt(97)
	fmt.Println(s.String())
	fmt.Println(i.Ascii())  //将数字转为ascii
	fmt.Println(i.String()) //将数字作为字符串输出
}

细节:

实际操作下来其实也没有说很难理解,只是可能类型转换那个地方卡了一下导致难以理解而已

重点是看懂这里的代码:
var s Stringer = MyString("传递参数string")  
var i Stringer = MyInt(97)
接口类型接收实现了接口方法的类型,然后就能够调用接口方法了,就这么理解就行了。

在MyString和MyInt中都是强制类型转换,将string字符和int数字转为对应的别名,然后给到变量后就能直接使用实现了接口的方法,因为已经转为了那两个基本类型了。
在强调一遍:本身是没有我们定义的这种类型的,所以要强制转换,然后接口其实就能随便写了,管你要不要用他这个值。
条件:
属于这个类型(强制类型转换)
实现了接口方法(正常实现接口方法)
调用就直接调用即可(正常)

切片实现接口

其实到了切片实现接口就很容易理解了

我发现其实是你这个类型实现了这个接口后,就可以用了

我发现要用这个方法只是仅仅的需要你是这个类型,而不是说在于什么强制类型转换,而是你要用这个接口方法是因为那个类型实现了这个接口啊,所以你要强制类型转换,所以要实例化这个类型啊,确实有点悟道了,也有点不明白我之前到底为啥会卡住。

切片实现接口也很简单,到这里其实已经不分什么结构体、基本类型、切片的了,本质就是你实例化一个实现了接口的类型,然后你的某个类型实现了接口类型的方法,那么你就直接实例化给到接口类型就拿这个类型去调用方法就行了。 (有点抽象,还是直接看代码吧)

type I_slice interface { //接口类型
	sum() int //返回切片的和
}
type MySlice []int //切片类型,换一个intslice别名而已,方便自定义,且实现接口方法

func (ms I_slice) sum() int { //就是自己定义的类型实现了接口类型而已,很好理解
	s := 0
	for _, v := range ms {
		s += v
	}
	return s
}

func main() {
	var s I_slice = MySlice{1, 2, 3, 4, 5} //我们自己自定义的切片类型然后赋值给接口类型
    
     //var s MySlice = MySlice{1, 2, 3, 4, 5} 
    //var s = MySlice{1, 2, 3, 4, 5} //这两其实也可以,就是我们自定义的类型本来就是有实现这个方法的,只不过你没有赋值给接口类型,所以不算实现了接口的方法而已,但是你本身就拥有的方法当然可以用啦
	fmt.Println(s.sum())
}

接口练习

项⽬描述

创建⼀个形状计算器,它可以计算不同形状的⾯积。需要定义⼀个 Shape 接⼝,
并为不同的形状(如圆形和矩形)实现这个接⼝。
然后编写⼀个函数来计算并打印这些形状的⾯积。 

步骤
定义⼀个 Shape 接⼝,包含⼀个 area ⽅法。 
创建⼀个 Circle 结构体,并实现 Shape 接⼝。 
创建⼀个 Rectangle 结构体,并实现 Shape 接⼝。 
编写⼀个函数 printArea,接受⼀个 Shape 类型的参数,并打印它的⾯积。 
在 main 函数中创建⼀些 Circle 和 Rectangle 实例,并调⽤ printArea 函数来打印它们的⾯积。

示例代码

type Shape interface {
	Area() float64
}

type Circle struct {
	radius float64
}

type Rectangle struct {
	width  float64
	height float64
}

func (c Circle) Area() float64 {
	return (c.radius * c.radius * math.Pi)
}
func (r Rectangle) Area() float64 {
	return (r.width * r.height)
}

func printArea(s Shape) {
	/*
		这里实际上就相当于强制类型转换了,
		因为两个结构体实现了Area方法,
		那么就强制类型转换为Shape后能够在函数中正常使用该方法
	*/
	fmt.Printf("%.2f\n", s.Area())
}
func main() {d
	c := Circle{radius: 2}
	r := Rectangle{width: 2, height: 4}
	printArea(c)
	printArea(r)
}

Embed嵌入文件

只支持嵌入为string, byte切片和embed.FS三种类型。相对来说比较难理解的是embed.FS

使⽤//go:embed后,下⽅必须是全局变量

使用embed可以嵌⼊⽂件夹下的⽂件,使⽤通配符*,比如static/*

示例代码:

image-20241122201153813

package main

import (
	"embed"
	_ "embed"
	"fmt"
)

//go:embed a.txt
var a string

//go:embed static/1.txt
var s []byte

//go:embed static/*
var f1 embed.FS

//go:embed static/* static/2.txt
var f2 embed.FS

//go:embed static/1.txt
//go:embed static/2.txt
//go:embed static/3.txt
var f3 embed.FS

func main() {
	fmt.Println("============string接收================")
	fmt.Println(a)
	fmt.Println("============byte接收================")
	fmt.Printf("%q\n", s)
	fmt.Println(string(s))
	fmt.Println("============FS单个文件================")
	data, _ := f1.ReadFile("static/1.txt")
	fmt.Println(string(data))
	fmt.Println("============FS目录,当前目录等等多个文件,go:embed空格隔开================")
	data, _ = f2.ReadFile("static/2.txt")
	fmt.Println(string(data))
	fmt.Println("============FS多个文件,go:embed可以不用空格隔开================")
	data, _ = f3.ReadFile("static/3.txt")
	fmt.Println(string(data))
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IP----访问服务器流程

这只是IP的其中一块内容-访问服务器流程,IP还有更多内容可以查看IP专栏,前一段学习内容为IA内容,还有更多内容可以查看IA专栏,可通过以下路径查看IA-----配置NAT-CSDN博客CSDN,欢迎指正 1.访问服务器流程 1.分层 1.更利于标准化…

阿里云ack的创建与实战应用案例

阿里云ack的创建与应用案例 创建前开通ack相关服务:开始创建简单的魔方游戏,熟悉sv与clb自动注册创建部署一个nginx 服务示例:走不同域名访问不同svc资源:为什么需要 Ingress ?创建第一个域名的 Deployment和Service。…

llama.cpp 一键运行本地大模型 - Windows

文章目录 llama.cpp 一键运行本地大模型 - Windows嘿,咱来唠唠 llama.cpp 这玩意儿!gguf 格式是啥?咱得好好说道说道基座模型咋选?所需物料,咱得准备齐全咯核心命令,得记牢啦运行方式咋选?测试应…

BUU40 [CSCCTF 2019 Qual]FlaskLight1【SSTI】

模板: {{.__class__.__base__.__subclasses__()[80].__init__.__globals__[__builtins__].eval("__import__(os).popen(type flag.txt).read()")}} 是个空字符串,.__class__代表这个空字符串的类是什么(这里是单引号双引号都行&a…

C# | GDI+图像测距辅助线的实现思路

C# | GDI图像测距辅助线的实现思路 文章目录 C# | GDI图像测距辅助线的实现思路一、辅助线需求概述二、坐标系与角度计算2.1 笛卡尔坐标系2.2 线长和角度计算方法2.3 文本角度矫正计算方法2.4 坐标变换实现步骤 三、与if判断方式对比四、总结 一、辅助线需求概述 在图像测量工…

免费使用 DeepSeek API 教程及资源汇总

免费使用 DeepSeek API 教程及资源汇总 一、DeepSeek API 资源汇总1.1 火山引擎1.2 百度千帆1.3 阿里百炼1.4 腾讯云 二、其他平台2.1 华为云2.2 硅基流动 三、总结 DeepSeek-R1 作为 2025 年初发布的推理大模型,凭借其卓越的逻辑推理能力和成本优势,迅速…

java后端开发day20--面向对象进阶(一)--static继承

(以下内容全部来自上述课程) 1.static–静态–共享 static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量。 1.静态变量 被static修饰的成员变量,叫做静态变量。 特点: 被该类…

Spring Boot集成MyBatis访问MySQL:从项目搭建到基础数据库查询(基础入门)

Spring Boot集成MyBatis访问MySQL 一、引言 在当今企业级应用开发中,Spring Boot、MyBatis与MySQL的组合凭借其高效性和灵活性,成为构建数据驱动型应用的首选方案。本文将带你从零开始搭建项目,掌握Spring Boot集成MyBatis的基础入门内容。…

ShenNiusModularity项目源码学习(14:ShenNius.Infrastructure项目分析)

ShenNius.Infrastructure项目用于定义ShenNius.Admin.Mvc项目和ShenNius.Admin.API项目共用的特性类、数据操作接口实现类、上下文类、通讯类,主要文件的用途如下:   Attributes文件夹保存特性类或过滤器类定义,主要包括:   …

【C语言】第八期——指针、二维数组与字符串

目录 1 初始指针 2 获取变量的地址 3 定义指针变量、取地址、取值 3.1 定义指针变量 3.2 取地址、取值 4 对指针变量进行读写操作 5 指针变量作为函数参数 6 数组与指针 6.1 指针元素指向数组 6.2 指针加减运算(了解) 6.2.1 指针加减具体数字…

Spring Boot集成Spring Security之HTTP请求授权

一、HTTP请求授权工作原理 ​ 基于Spring Security最新的Http请求授权讲解,不再使用旧版的请求授权 授权过滤器AuthorizationFilter获取认证信息 调用RequestMatcherDelegatingAuthorizationManager的check方法验证该用户是否具有该请求的授权 RequestMatcherDele…

可以免费无限次下载PPT的网站

前言 最近发现了一个超实用的网站,想分享给大家。 在学习和工作的过程中,想必做PPT是一件让大家都很头疼的一件事。 想下载一些PPT模板减少做PPT的工作量,但网上大多精美的PPT都是需要付费才能下载使用。 即使免费也有次数限制&#xff0…

从零开始自主「起身站立」,上海AI Lab发布最新控制算法,机器人:起猛了

来源 | 机器之心 近日,上海 AI Lab 具身智能中心研究团队在机器人控制领域取得了最新突破,提出的 HoST(Humanoid Standing-up Control)算法,成功让人形机器人在多种复杂环境中实现了自主站起,并展现出强大…

Solr中得Core和Collection的作用和关系

Solr中得Core和Collection的作用和关系 一, 总结 在Apache Solr中,Core和Collection 是两个核心概念,他们分别用于单机模式和分布式模式(SolrCloud)中,用于管理和组织数据。 二,Core 定义&am…

AI驱动的自动化留给人类的时间不多了

时间紧迫!时间紧迫!时间紧迫! 关于AI工作流催生的行业任务自动化时间窗口,结合技术成熟度、成本效益、行业特性等维度,可划分为以下阶段: 一、技术渗透阶段(2025-2028年) 高重复性任…

使用 Containerd 通过 HTTP 协议拉取 Harbor 私有镜像仓库的镜像

在 Kubernetes 1.24及以上版本环境中,docker不再被支持,主要使用Containerd 是常用的容器运行。默认情况下,Containerd 使用 HTTPS 协议与镜像仓库通信。然而,在某些场景下(如测试环境或内部网络)&#xff…

Dify在Ubuntu20.04系统的部署

文章目录 一、dify 介绍1.核心功能优势2.应用场景 二、dify 安装(docker方式)1.代码库下载2.配置文件修改3.启动docker 容器 三、遇到问题与解决1.使用sudo docker compose up -d报错2.使用service docker start报错 一、dify 介绍 Dify 是一款开源的大语言模型(LL…

达梦:内存相关参数

目录 28个相关参数1. 内存池相关MEMORY_POOLMEMORY_N_POOLSMEMORY_BAK_POOL 2. 大缓冲区相关HUGE_BUFFERHUGE_BUFFER_POOLS 3. 共享缓冲区相关BUFFERBUFFER_POOLSBUFFER_MODEMAX_BUFFER 4. 快速池相关FAST_POOL_PAGES 5. 回收池相关RECYCLE_POOLS 6. 回滚段池相关ROLLSEG_POOLS…

计算机毕设-基于springboot的融合多源高校画像数据与协同过滤算法的高考择校推荐系统的设计与实现(附源码+lw+ppt+开题报告)

博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

《Qt动画编程实战:轻松实现头像旋转效果》

《Qt动画编程实战:轻松实现头像旋转效果》 Qt 提供了丰富的动画框架,可以轻松实现各种平滑的动画效果。其中,旋转动画是一种常见的 UI 交互方式,广泛应用于加载指示器、按钮动画、场景变换等。本篇文章将详细介绍如何使用 Qt 实现…