盘点Go语言中那些酷酷的语法

虽然写Go语言已经一年有余,认识Go语言已经近三年,但是写Go代码的快乐并未随着时间的推移而逐渐消沉,有时仍然会因为写一段伶俐的代码而感到很酷,所以想专门写一篇基础性的文章,来记录一下Go语言中那些很酷的语法,非常适合Go语言的新手同学哦!

列个大纲先看下具体有哪些吧:

  • 少即是多:简短变量声明和函数返回值屏蔽
  • 追逐lambda:什么是匿名函数和闭包
  • defer语法:将函数放在栈中延迟执行
  • go关键字:嗖!的一下go出一个协程
  • Format函数:日期格式化的有趣约定
  • 错误处理:没有try...catch...一样可以游刃有余
  • 使用通信的方式来共享内存:高效的channel

以下是正文:

少即是多:简短变量声明和函数返回值屏蔽

知道Go语言是有多么简洁了吧:

go 复制代码
func add(i int) (int, error) {
   return i + 1, nil
}

func TestLessIsMore(t *testing.T) {
   //声明变量i
   i := 10
   
   j, _ := add(i) //屏蔽err返回值
   fmt.Println(j)
   
   _, _ = add(i) //屏蔽全部返回值
}

追逐lambda:什么是匿名函数和闭包

匿名函数、闭包和lambda三者是完全不同的三个概念,但是又有着相互的关联。

首先是匿名函数,它是指在代码中定义的没有名字的函数。匿名函数可以被赋值给变量,也可以作为参数传递给其他函数。

闭包是指一个函数内部引用了外部函数的变量,并且可以在函数外部被调用或访问时,依然能够保持对这些变量的访问能力。换句话说,闭包是一个函数以及其引用的外部变量的组合体。

在Go语言中,闭包的实现非常简洁。当一个函数内部定义了一个匿名函数,并且这个匿名函数引用了外部函数的变量时,Go语言会自动将这些变量捕获到闭包中。通过闭包,我们可以在函数外部对这些变量进行访问、修改等操作,即使外部函数已经执行完毕,这些变量的值也会被保留。

lambda表达式是一种函数式编程的概念,它是一种匿名函数的简洁语法表示。在一些编程语言中,如Python、JavaScript等,lambda表达式可以用一种紧凑的方式定义一个匿名函数。通常情况下,lambda表达式用于编写简单的、单行的函数逻辑。

虽然匿名函数和lambda表达式有相似之处,都是指没有名称的函数,但是它们的使用场景和语法细节可能会有所不同。在Go语言中,匿名函数的定义比较灵活,可以包含多行代码,有更多的语法支持;而lambda表达式通常用于简单的、单行的函数逻辑。另外,Go语言中没有直接对应于lambda表达式的语法,但可以通过匿名函数来实现类似的功能。

匿名函数和lambda:

go 复制代码
type operation func(int, int) int

func calculate(a, b int, op operation) int {
	return op(a, b)
}

func TestFun(t *testing.T) {
	//匿名函数
	f := func(i, j int) int {
		return j + i
	}
	f(1, 1)

	// lambda函数示例
	add := func(a, b int) int {
		return a + b
	}

	subtract := func(a, b int) int {
		return a - b
	}

	result1 := calculate(5, 3, add)
	fmt.Println(result1) // 输出:8

	result2 := calculate(5, 3, subtract)
	fmt.Println(result2) // 输出:2
}

Go实现闭包:

go 复制代码
func TestBiBao(t *testing.T) {
   // 外部函数
   outer := func() {
      // 外部变量
      count := 0

      // 内部函数(闭包)
      inner := func() {
         count++
         fmt.Println(count)
      }

      // 调用内部函数
      inner()
   }

   // 调用外部函数
   outer() // 输出:1
   outer() // 输出:1
}

在上面的代码中,我们定义了一个外部函数outer,它内部定义了一个匿名函数innerinner引用了外部函数outer的变量count。每次调用outer函数时,都会创建一个新的闭包,这个闭包包含了独立的count变量。

在调用outer函数时,内部函数inner会被调用,并对count变量进行递增操作,并输出结果。由于闭包捕获了外部变量,所以每次调用内部函数时,都能正确地访问和修改之前的count值。

需要注意的是,在每次调用outer函数时,都会创建一个新的闭包,因此每次调用时的count变量都是独立的。这就是闭包的特性之一,它可以在函数调用之间保持状态。

闭包在一些场景下非常有用,例如:

  1. 保存状态:通过闭包,可以在函数调用之间保持某些状态。每次调用闭包函数时,可以继续使用之前的状态,而不需要将状态传递给函数的参数。
  2. 实现私有变量:通过在闭包内定义的变量,可以实现一种私有化的效果,外部无法直接访问或修改这些变量,只能通过闭包函数提供的接口进行操作。
  3. 回调函数:可以将匿名函数作为参数传递给其他函数,并在需要时进行调用。通过闭包,可以将上下文信息传递给回调函数,实现更灵活的函数调用。

需要注意的是,闭包引用的外部变量在闭包函数内部可能会被修改,因此在使用闭包时需要注意变量的生命周期和可变性。

defer语法:将函数放在栈中延迟执行

go defer 是 Go 语言中的语句,它用于在函数返回之前执行一些代码。使用 defer 语句可以确保在函数执行完毕后,一些必要的清理工作或者收尾工作被执行。

go 复制代码
func TestDefer(t *testing.T) {
   defer func() {
      fmt.Println("defer func ...")
   }()
   fmt.Println("no defer ...")
}

go语法:嗖!的一下go出一个协程

Go最厉害的特性之一,可以看下我这篇文章的详解:深入浅出Go并发之协程---goroutine

go 复制代码
func TestGoroutine(t *testing.T) {
	group := sync.WaitGroup{}
	group.Add(1)

	go func() {
		fmt.Println("this is go Sou fun ...")
		group.Done()
	}()
	
	group.Wait()
}

Format函数:日期格式化的有趣约定

先看代码,这是Go语言中将日期转换为YYYY-MM-DD HH:MM:SS的函数,当时二话没说就搜了下为什么要这样写,很快就得出了答案,是因为好记:sweat_smile:,在国外一般都是把年份放在最后,所以我们吧年份挪到最后就会发现,可以使用010203040506这样的方式记下来,貌似确实很好记......

go 复制代码
func TestTimeFormat(t *testing.T) {
   //12小时制
   format := time.Now().Format("2006-01-02 03:04:05")
   fmt.Println(format)

   //24小时制
   format2 := time.Now().Format("2006-01-02 15:04:05")
   fmt.Println(format2)
}

错误处理:没有try...catch...一样可以游刃有余

Go语言中的异常处理机制可以让我们在程序中捕获和处理错误和异常情况,而不是使用try...catch...这样的异常块来缓存异常。这样的设计可以让我们更灵活地处理错误和异常,并且可以更好地控制程序的流程。

与此同时,使用defer+recover()可以让我们捕获程序中可能发生的异常。

go 复制代码
func TestRecover(t *testing.T) {
	f := func() {
		panic("has err ...")
	}

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	f()
}

使用通信的方式来共享内存:高效的channel

在Go语言中,通道(channel)是一种用于在goroutine之间进行通信的特殊类型。通道提供了一种方式,可以在goroutine之间传递值,这使得并发编程变得更加简单和有效。

下面是一些使用channel的方式:

  • 并发求和:使用通道可以轻松地实现并发求和。在常规的求和中,所有的计算都在同一个goroutine中执行,而使用通道可以在多个goroutine中并行执行计算。
go 复制代码
func sum(n int) int {  
    sum := 0  
    for i := 0; i < n; i++ {  
        sum += i  
    }  
    return sum  
}  
  
func main() {  
    n := 1000000  
    c := make(chan int)  
    for i := 0; i < 10; i++ {  
        go func() {  
            c <- sum(n)  
        }()  
    }  
    for i := 0; i < 10; i++ {  
        sum := <-c  
        fmt.Println(sum)  
    }  
}
  • 并发队列:使用通道可以轻松地实现并发队列。在常规的队列中,所有的操作都在同一个goroutine中执行,而使用通道可以在多个goroutine中并行执行操作。
go 复制代码
func main() {  
    c := make(chan int)  
    for i := 0; i < 10; i++ {  
        go func(i int) {  
            c <- i  
        }(i)  
    }  
    for i := 0; i < 10; i++ {  
        fmt.Println(<-c)  
    }  
}

最后

好了,以上就是我总结出的Go语言中那些很酷的语法,当然想要将这些优雅的操作进行灵活的使用并非易事,而且Go语言中也存在其他非常优秀的语法和设计模式,欢迎各位读者在评论区进行分享和讨论。

相关推荐
用户743835613516 小时前
无锁 Hub:我的 IM 系统为什么用 channel 而不是 mutex 管理在线用户
go
吴佳浩1 天前
Go史上最大“打脸”现场来了:泛型方法终于实现了
后端·go
明月_清风2 天前
深入 Go 并发编程:从 Goroutine 到 Channel 的系统性避坑指南
后端·go
用户34232323763172 天前
开源!Go+Wails+Vue3 手搓一个 PLC 实时监控桌面工具
go
止语Lab2 天前
为什么你的 Go TCP server P99 延迟这么高
go
Andy Dennis3 天前
nsq学习记录
消息队列·go·nsq
韦胖漫谈IT3 天前
选语言不是站队,是选适合问题的工具
java·python·ai·rust·go·技术落地
喵个咪3 天前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
夜悊3 天前
Go网络编程的学习代码示例:客户端/服务端(C/S)模型
go