Go语言提升开发效率的语法糖技巧分享

目录
  • 前言
  • 可变长参数
  • 声明不定长数组
  • init函数
  • 忽略导包
  • 忽略字段
  • json序列化忽略某个字段
  • json序列化忽略空值字段
  • 短变量声明
  • 类型断言
  • 切片循环
  • 判断map的key是否存在
  • select控制结构

前言

哈喽,大家好,我是asong。

每门语言都有自己的语法糖,像java​的语法糖就有方法变长参数、拆箱与装箱、枚举、for-each​等等,Go​语言也不例外,其也有自己的语法糖,掌握这些语法糖可以助我们提高开发的效率,所以本文就来介绍一些Go语言的语法糖,总结的可能不能全,欢迎评论区补充。

可变长参数

Go​语言允许一个函数把任意数量的值作为参数,Go语言内置了...操作符,在函数的最后一个形参才能使用...操作符,使用它必须注意如下事项:

  • 可变长参数必须在函数列表的最后一个;
  • 把可变长参数当切片来解析,可变长参数没有没有值时就是​
  • 可变长参数的类型必须相同

既然我们的函数可以接收可变长参数,那么我们在传参的时候也可以传递切片使用...进行解包转换为参数列表,append方法就是最好的例子:

var sl []int
sl = append(sl, 1)
sl = append(sl, sl...)

append方法定义如下:

// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
func append(slice []Type, elems ...Type) []Type

声明不定长数组

数组是有固定长度的,我们在声明数组时一定要声明长度,因为数组在编译时就要确认好其长度,但是有些时候对于想偷懒的我,就是不想写数组长度,有没有办法让他自己算呢?当然有,使用...操作符声明数组时,你只管填充元素值,其他的交给编译器自己去搞就好了;

a := [...]int{1, 3, 5} // 数组长度是3,等同于 a := [3]{1, 3, 5}

有时我们想声明一个大数组,但是某些index想设置特别的值也可以使用...操作符搞定:

a := [...]int{1: 20, 999: 10} // 数组长度是100, 下标1的元素值是20,下标999的元素值是10,其他元素值都是0

init函数

Go​语言提供了先于main​函数执行的init​函数,初始化每个包后会自动执行init​函数,每个包中可以有多个init​函数,每个包中的源文件中也可以有多个init函数,加载顺序如下:

从当前包开始,如果当前包包含多个依赖包,则先初始化依赖包,层层递归初始化各个包,在每一个包中,按照源文件的字典序从前往后执行,每一个源文件中,优先初始化常量、变量,最后初始化init​函数,当出现多个init函数时,则按照顺序从前往后依次执行,每一个包完成加载后,递归返回,最后在初始化当前包!

init​函数实现了sync.Once​,无论包被导入多少次,init​函数只会被执行一次,所以使用init​可以应用在服务注册、中间件初始化、实现单例模式等等,比如我们经常使用的pprof​工具,他就使用到了init​函数,在init函数里面进行路由注册:

//go/1.15.7/libexec/src/cmd/trace/pprof.go
func init() {
 http.HandleFunc("/io", serveSVGProfile(pprofByGoroutine(computePprofIO)))
 http.HandleFunc("/block", serveSVGProfile(pprofByGoroutine(computePprofBlock)))
 http.HandleFunc("/syscall", serveSVGProfile(pprofByGoroutine(computePprofSyscall)))
 http.HandleFunc("/sched", serveSVGProfile(pprofByGoroutine(computePprofSched)))

 http.HandleFunc("/regionio", serveSVGProfile(pprofByRegion(computePprofIO)))
 http.HandleFunc("/regionblock", serveSVGProfile(pprofByRegion(computePprofBlock)))
 http.HandleFunc("/regionsyscall", serveSVGProfile(pprofByRegion(computePprofSyscall)))
 http.HandleFunc("/regionsched", serveSVGProfile(pprofByRegion(computePprofSched)))
}

忽略导包

Go语言在设计师有代码洁癖,在设计上尽可能避免代码滥用,所以Go​语言的导包必须要使用,如果导包了但是没有使用的话就会产生编译错误,但有些场景我们会遇到只想导包,但是不使用的情况,比如上文提到的init​函数,我们只想初始化包里的init函数,但是不会使用包内的任何方法,这时就可以使用  _   操作符号重命名导入一个不使用的包:

import _ "github.com/asong"

忽略字段

在我们日常开发中,一般都是在屎上上堆屎,遇到可以用的方法就直接复用了,但是这个方法的返回值我们并不一定都使用,还要绞尽脑汁的给他想一个命名,有没有办法可以不处理不要的返回值呢?当然有,还是 _ 操作符,将不需要的值赋给空标识符:

_, ok := test(a, b int)

json序列化忽略某个字段

大多数业务场景我们都会对struct​做序列化操作,但有些时候我们想要json​里面的某些字段不参加序列化,-操作符可以帮我们处理,Go语言的结构体提供标签功能,在结构体标签中使用 - 操作符就可以对不需要序列化的字段做特殊处理,使用如下:

type Person struct{
  name string `json:"-"`
  age string `json: "age"`
}

json序列化忽略空值字段

我们使用json.Marshal​进行序列化时不会忽略struct​中的空值,默认输出字段的类型零值(string​类型零值是"",对象类型的零值是nil​...),如果我们想在序列化时忽略掉这些没有值的字段时,可以在结构体标签中中添加omitempty tag:

type User struct {
 Name  string   `json:"name"`
 Email string   `json:"email,omitempty"`
  Age int        `json: "age"`
}

func test() {
 u1 := User{
  Name: "asong",
 }
 b, err := json.Marshal(u1)
 if err != nil {
  fmt.Printf("json.Marshal failed, err:%v\n", err)
  return
 }
 fmt.Printf("str:%s\n", b)
}

运行结果:

str:{"name":"asong","Age":0}

Age​字段我们没有添加omitempty​ tag在json​序列化结果就是带空值的,email字段就被忽略掉了;

短变量声明

每次使用变量时都要先进行函数声明,对于我这种懒人来说是真的不想写,因为写python​写惯了,那么在Go​语言是不是也可以不进行变量声明直接使用呢?我们可以使用 name := expression 的语法形式来声明和初始化局部变量,相比于使用var声明的方式可以减少声明的步骤:

var a int = 10
//等用于
a := 10
  • 使用短变量声明时有两个注释事项:
  • 短变量声明只能在函数内使用,不能用于初始化全局变量
  • 短变量声明代表引入一个新的变量,不能在同一作用域重复声明变量

多变量声明中如果其中一个变量是新变量,那么可以使用短变量声明,否则不可重复声明变量;

类型断言

我们通常都会使用interface​,一种是带方法的interface​,一种是空的interface,Go1.18​之前是没有泛型的,所以我们可以用空的interface{}​来作为一种伪泛型使用,当我们使用到空的interface{}作为入参或返回值时,就会使用到类型断言,来获取我们所需要的类型,在Go语言中类型断言的语法格式如下:

value, ok := x.(T)
or
value := x.(T)

x是interface​类型,T是具体的类型,方式一是安全的断言,方式二断言失败会触发panic;这里类型断言需要区分x​的类型,如果x是空接口类型:

空接口类型断言实质是将​eface​中​_type​与要匹配的类型进行对比,匹配成功在内存中组装返回值,匹配失败直接清空寄存器,返回默认值。

如果x是非空接口类型:

非空接口类型断言的实质是 iface 中 ​*itab​ 的对比。​*itab​ 匹配成功会在内存中组装返回值。匹配失败直接清空寄存器,返回默认值。

切片循环

切片/数组是我们经常使用的操作,在Go​语言中提供了for range语法来快速迭代对象,数组、切片、字符串、map、channel等等都可以进行遍历,总结起来总共有三种方式:

// 方式一:只遍历不关心数据,适用于切片、数组、字符串、map、channel
for range T {}

// 方式二:遍历获取索引或数组,切片,数组、字符串就是索引,map就是key,channel就是数据
for key := range T{}

// 方式三:遍历获取索引和数据,适用于切片、数组、字符串,第一个参数就是索引,第二个参数就是对应的元素值,map 第一个参数就是key,第二个参数就是对应的值;
for key, value := range T{}

判断map的key是否存在

Go语言提供语法 value, ok := m[key]​来判断map​中的key是否存在,如果存在就会返回key所对应的值,不存在就会返回空值:

import "fmt"

func main() {
    dict := map[string]int{"asong": 1}
    if value, ok := dict["asong"]; ok {
        fmt.Printf(value)
    } else {
      fmt.Println("key:asong不存在")
    }
}

select控制结构

Go​语言提供了select​关键字,select​配合channel​能够让Goroutine​同时等待多个channel​读或者写,在channel​状态未改变之前,select​会一直阻塞当前线程或Goroutine。先看一个例子:

func fibonacci(ch chan int, done chan struct{}) {
 x, y := 0, 1
 for {
  select {
  case ch <- x:
   x, y = y, x+y
  case <-done:
   fmt.Println("over")
   return
  }
 }
}
func main() {
 ch := make(chan int)
 done := make(chan struct{})
 go func() {
  for i := 0; i < 10; i++ {
   fmt.Println(<-ch)
  }
  done <- struct{}{}
 }()
 fibonacci(ch, done)
}

select与switch​具有相似的控制结构,与switch​不同的是,select​中的case​中的表达式必须是channel​的收发操作,当select​中的两个case​同时被触发时,会随机执行其中的一个。为什么是随机执行的呢?随机的引入就是为了避免饥饿问题的发生,如果我们每次都是按照顺序依次执行的,若两个case​一直都是满足条件的,那么后面的case永远都不会执行。

上面例子中的select​用法是阻塞式的收发操作,直到有一个channel​发生状态改变。我们也可以在select​中使用default​语句,那么select语句在执行时会遇到这两种情况:

  • 当存在可以收发的Channel​时,直接处理该Channel​ 对应的case;
  • 当不存在可以收发的Channel​ 时,执行default 中的语句;

注意:​nil channel​上的操作会一直被阻塞,如果没有​default case​,只有​nil channel​的​select​会一直被阻塞。

以上就是Go语言提升开发效率的语法糖技巧分享的详细内容,更多关于Go语言语法糖的资料请关注我们其它相关文章!

(0)

相关推荐

  • 几个小技巧帮你实现Golang永久阻塞

    目录 使用 sync.WaitGroup 空 select 死循环 用 sync.Mutex os.Signal 空 channel 或者 nil channel 总结 参考 Go 的运行时的当前设计,假定程序员自己负责检测何时终止一个 goroutine 以及何时终止该程序.可以通过调用 os.Exit 或从 main() 函数的返回来以正常方式终止程序.而有时候我们需要的是使程序阻塞在这一行. 使用 sync.WaitGroup 一直等待直到 WaitGroup 等于 0 package m

  • Go本地测试解耦任务拆解及沟通详解

    目录 解耦 no情绪 & todolist 及时沟通 找对人 Go本地测试的思路 我习惯在开发过程中及时测试自己开发的功能模块,这样能及时发现问题,节省后期功能耦合之后,debug的时间. 为了统一管理要测试的功能(模块),所以创建了测试类,在cmd中直接运行,不需要借助postman等接口请求工具. fun Run(){ //测试方法 TestUnifyInputInsert() } func TestUnifyInputInsert() { var req *goods_unify.Good

  • Go批量操作excel导入到mongodb的技巧

    目录 1. 选择命令行包 2. 读取配置,连接数据库 3. 读取文件 3.1. 并发读 3.2. 使用excelize处理excel 3.3. 使用mpb在命令行输出进度显示 4. 写入mongodb 5. 同步mysql 6. 总结 需求:完成一个命令工具,批量处理某个目录下面的一些excel,将这些excel数据导入到mongodb,同时可以同步到mysql :: 花了一天时间写完代码,代码库位置:https://gitee.com/foz/lib/tree/master/ecc 代码目录:

  • 详解go语言json的使用技巧

    本文整理了一部分我们平时在项目中经常遇到的关于go语言JSON数据与结构体之间相互转换的问题及解决办法. 基本的序列化 首先我们来看一下Go语言中json.Marshal()(系列化)与json.Unmarshal(反序列化)的基本用法. type Person struct { Name string Age int64 Weight float64 } func main() { p1 := Person{ Name: "小明", Age: 18, Weight: 71.5, }

  • Go语法糖之‘...’ 的使用实例详解

    语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会. 下面通过例子看下Go语法糖之'...' 的使用,具体内容如下所示: '-' 其实是go的一种语法糖. 它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数. 第二个用法是sli

  • Go应用中优雅处理Error的技巧总结

    目录 前言 用户自定义类型 上下文数据 原因(Causes) 组件(Component) 响应类型(ResponseType) 重试 GoError 接口 抽象error 结论 前言 Go语言很强大并且现在也十分流行 - 许多项目都是用Go语言来实现的,如Kubernetes.Go语言的一个有趣特性是它的多值返回功能提供了一种与其他编程语言不同的错误处理方法. Go将error视为具有预定义类型的值,其本身是一个interface类型.然而,编写多层体系结构应用程序并使用api暴露应用的特性需要

  • Go语言提升开发效率的语法糖技巧分享

    目录 前言 可变长参数 声明不定长数组 init函数 忽略导包 忽略字段 json序列化忽略某个字段 json序列化忽略空值字段 短变量声明 类型断言 切片循环 判断map的key是否存在 select控制结构 前言 哈喽,大家好,我是asong. 每门语言都有自己的语法糖,像java​的语法糖就有方法变长参数.拆箱与装箱.枚举.for-each​等等,Go​语言也不例外,其也有自己的语法糖,掌握这些语法糖可以助我们提高开发的效率,所以本文就来介绍一些Go语言的语法糖,总结的可能不能全,欢迎评论

  • 强烈推荐这些提升代码效率的IDEA使用技巧

    一.SVN的集成 IDEA默认集成了对Svn的支持 . File ->Setting 直接设置执行程序即可. 注意:如果设置之后依然无法使用svn,是因为安装SVN的时候没有选择命令行工具. 解决方法:重装SVN,配置项重新选择command line client tools 即可. 二.开启热更新 有发现不少同学不知道热更新,还在为了调一行代码重启服务器,然后调试,效率太低,开启热更新,实时修改代码,实时看效果. 具体步骤: File ->Settings -Build -> Com

  • chrome开发者助手插件v2.10发布提升开发效率不再只是口号

    chrome开发者助手插件v2.10发布了,这个版本重点提升了常用工具的使用效率: 插件下载地址 1. 新标签页支持一键打开常用小工具 开发同学使用比较频繁的IP查询.二维码转换.时间戳转换.文档查询.翻译等小工具,可以在桌面一键打开使用 2. 新标签页支持文件夹,内置开发者工具箱 桌面可以建文件夹了,大家可以通过文件夹来分类管理常用网址.小工具了.而且已经为大家梳理了开发者工具箱方便大家使用.非新用户可以到系统分类里面添加到桌面上 3. 浏览器工具栏有快捷入口了 通过浏览器设置可以打开快捷入口

  • JS 4个超级实用的小技巧 提升开发效率

    目录 1.短路判断 2.可选链操作符 ( ? ) 3.空值合并操作符 ( ?? ) 4.return终止函数 1.短路判断 当只需要简单的if条件时,可使用此方法 let x = 0; let foo = () => console.log('执行了'); if(x === 0){ foo() } 通过使用&&运算符来实现同样的if功能,如果&&之前的条件为false,则&&之后的代码将不会执行. let x = 0; let foo = () =&g

  • 56个实用的JavaScript 工具函数助你提升开发效率

    目录 1. 数字操作 (1)生成指定范围随机数 2. 数组操作 (1)数组乱序 (2)数组扁平化 (3)数组中获取随机数 3. 字符串操作 (1)生成随机字符串 (2)字符串首字母大写 (3)手机号中间四位变成* (4)驼峰命名转换成短横线命名 (5)短横线命名转换成驼峰命名 (6)全角转换为半角 (7)半角转换为全角 4. 格式转化 (1)数字转化为大写金额 (2)数字转化为中文数字 5. 操作存储 (1)存储loalStorage (2)获取localStorage (3)删除localSt

  • AJAX 图片展示框架56个 提升开发效率第1/2页

    动态图片展示一方面能使用AJAX或Flash实现图片数据的异步获取,减少浏览的等待时间:一方面使用JavaScript+CSS定制友好的用户体验模式,是图片展览web应用程序开发的一大利器.这里收集的框架都是经过测试可用的,描述大多是各框架主页上的介绍,来不及翻译(有些是西班牙语的 @_@),主要收集来源为欧美和台湾的一些社区和博客,希望大家也能将自己收集的好框架拿出来一起分享! Ajax Image Galleries & Lightboxes MinishowcaseMinishowcase

  • 帮你提高开发效率的JavaScript20个技巧

    目录 1. 申明和初始化数组 2.进行求和.最小值和最大值 3. 对字符串.数字或对象的数组进行排序 4. 是否需要从一个数组中过滤掉无用的值? 5. 为各种条件使用逻辑运算符 6. 删除重复的值 7. 创建一个计数器对象或Map 8. 三元运算符很酷 9. 与传统的once相比,for循环更快. 10. 合并两个对象 11. 箭头函数 12. 可选链式 13. 打乱一个数组 14. 空值合并运算符 15. Rest & Spread 运算符 16. 缺省参数 17. 将十进制转换为二进制或十六

  • JAVA语法糖原理你知道吗

    目录 Java语法糖 1.基本概念 1.1语法糖 1.2解语法糖 2.常见糖块 2.1 switch 支持 String 与枚举 2.2泛型 2.3自动装箱与拆箱 2.4方法变长参数 2.5枚举 2.6条件编译 2.7 for : each循环 3.最后 3.1Java编译与反编译 总结 Java语法糖 ​ 几乎所有的编程语言都或多或少提供过一些语法糖来方便程序员的代码开发,这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高效率,或能提升语法的严谨性,或能减少编码出错的机会. 1.基本概念

  • 弱类型语言javascript开发中的一些坑实例小结【变量、函数、数组、对象、作用域等】

    本文实例讲述了弱类型语言javascript开发中的一些坑.分享给大家供大家参考,具体如下: 测试1: (未声明变量自动提升为全局变量) test1(); function test1() { function setName() { name = '张三'; // 此处没有var声明,提升至全局 } setName(); console.log(name);// '张三' } 测试2: (函数内部局部变量的变量提升) test2(); function test2() { var a = 1;

  • 一起聊聊Go语言中的语法糖的使用

    目录 前言 进入正题 可变长参数 声明不定长数组 ... 操作符 切片循环 忽略变量.字段或者导包 短变量声明 另类的返回值 总结 前言 由于工作变动,我现在已经开始使用Golang了.用了一段时间之后,我发现Golang(后面简称Go)中的语法糖还蛮多的,有些语法糖还让会让人很懵逼.那么接下来,让我以一个曾经的 Java CURD boy,来说一说 Go 中的语法糖. 进入正题 至于什么是语法糖,名词解释我就不解释了,老司机自然是懂,新手司机想了解的可以去百度问一下.闲话少说我们直接开讲. 可

随机推荐