云原生时代崛起的编程语言Go基础实战

这篇具有很好参考价值的文章主要介绍了云原生时代崛起的编程语言Go基础实战。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

@

目录
  • 概述
    • 定义
    • 使用场景
    • Go 安全
  • 使用须知
    • 搜索工具
    • Go基础命令
    • 标准库
  • 基础语法
    • Effective Go 概览
    • 命名规范
    • 注释
    • 变量
    • 常量(const)
    • 控制结构
    • 数据类型
    • 迭代(range)
    • 函数
    • 指针
    • 字符串和符文
    • 结构体(struct)
    • 方法
    • 接口(interface)
    • 泛型
    • 错误(errors)
    • 恐慌(pinic)
    • 推迟(defer)
    • 恢复(recover)

概述

定义

Go 官网地址 https://golang.google.cn/ 最新版本1.20.3

Go 官网文档地址 https://golang.google.cn/doc/

Go 源码地址 https://github.com/golang

Golang简称Go,是谷歌开源的编程语言,旨在提供程序员编程效率,易于学习,非常适合团队使用,天然支持高并发,有垃圾收集机制,且自带功能完善健壮的标准库。

Go语言表现力强、简洁、干净、高效。它的并发机制使得其容易编写出充分利用多核和网络机器的程序,其类型系统使程序构造变得灵活和模块化。Go可以快速编译为机器码,并且具有垃圾收集的便利性和运行时反射的强大功能;是一种快速的、静态类型的编译语言,感觉像是一种动态类型的解释语言。

Go语言由于来自全球技术大厂谷歌创造及推动,其生态发展极其迅速,从业界声音看大有可能成为接下来10年内最具统治力的语言,也即是替代Java霸主地位,至于未来是否可以静待结果。至少从目前国内大厂阿里、腾讯、百度、字节的使用趋势极其效应可以看到其迅速扩张的可能,越受开发者喜爱其生态完整就会越好,如果从事企业级开发的伙伴有时间精力建议的话不烦可以开始深入学学Go语言开发。

使用场景

  • 云上和网络的应用:如云计算领域、区块链、并发网络编程,有主要的云提供商强大的工具和api生态系统,用Go构建服务很容易。下面列举小部分流行包:
    • cloud.google.com/go
    • aws/client
    • Azure/azure-sdk-for-go
  • Cli命令行接口:cli是纯文本的。云和基础设施应用程序主要基于cli,这样易于自动化和远程功能。使用流行的开放源码包和健壮的标准库,可以使用Go创建快速而优雅的cli。下面列举小部分流行包:
    • spf13/cobra
    • spf13/viper
    • urfave/cli
    • delve
    • chzyer/readline
  • Web开发:Go支持快速和可扩展的web应用程序。下面列举小部分流行包:
    • net/http
    • html/template
    • flosch/pongo2
    • database/sql
    • elastic/go-elasticsearch
  • 开发运营和网站可靠性工程(云原生运维方向,特别是基于k8s的运维开发):Go拥有快速的构建时间、简洁的语法、自动格式化器和文档生成器,可以同时支持DevOps和SRE。下面列举小部分流行包:
    • open-telemetry/opentelemetry-go
    • istio/istio
    • urfave/cli

Go 安全

Go语言编写安全可靠的软件,主要有如下信息:

  • Go安全策略:解释了如何向Go团队报告Go标准库和子库中的安全问题。
  • Go安全发布:Go版本历史包含了过去安全问题的发布说明。根据发布策略发布了两个最新的Go主要版本的安全补丁。
  • Go漏洞管理:支持帮助开发人员找到可能影响其Go项目的已知公共漏洞。
  • Go Fuzzing:提供一种自动测试,持续地操作程序的输入以发现错误。
  • Go加密:Go加密库是Go标准库和子库中的crypto/…和golang.org/x/crypto/…包,并遵循这些原则开发。

使用须知

搜索工具

Go 开发包搜索网站 https://pkg.go.dev/

先安装最新版本的Go。有关下载和安装Go编译器、工具和库的说明,请自行查找安装文档,版本下载(1.20.3),https://golang.google.cn/dl/

云原生时代崛起的编程语言Go基础实战

由于之前使用1.20.2安装,安装后查看版本

云原生时代崛起的编程语言Go基础实战

开发过程有需要了解相当包的使用,可以在Go开发官网上查询,这也是一个良好学习和使用方式。

云原生时代崛起的编程语言Go基础实战

IDE集成开发工具可以选择JetBrains的GoLand,一个扩展支持JavaScript, TypeScript和数据库的Go IDE,JetBrains Go地址

Go基础命令

# 通过命令行输入go可以查看支持命令
go
go help build

云原生时代崛起的编程语言Go基础实战

  • go bug:打开Bug报告。
  • go build:用于编译指定的源码文件或代码包以及它们的依赖包。常用
  • go clean:移除当前源码包和关联源码包里面编译生成的文件,即删除掉执行其它命令时产生的一些文件和目录,go 命令会在临时目录中构建对象,因此 go clean 主要关注其他工具或手动调用 go build 留下的对象文件。常用
  • go doc:打印与由其参数(包、const、func、类型、var、方法或结构字段)标识的项目相关联的文档注释。
  • go env:打印Go语言的环境信息。
  • go fix:把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。
  • go fmt:go程序格式化,自动对齐、空格等。如果用了IDE这个命令就不需要了。
  • go generate:⽣成由现有⽂件中的指令描述的运⾏命令。这些命令可以运⾏任何进程,但⽬的是创建或更新 Go 源文件。
  • go get:根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。常用
  • go install:用于编译并安装指定的代码包及它们的依赖包。常用
  • go list:列出指定的代码包的信息。
  • go mod
    • go mod init:生成go.mod文件。常用
    • go mod download :下载go.mod文件中指明的所有依赖。
    • go mod tidy:整理现有的依赖。常用
    • go mod graph:查看现有的依赖结构。
    • go mod edit:编辑go.mod文件。
    • go mod vendor :导出项目所有的依赖到vendor目录。
    • go mod verify:校验一个模块是否被篡改过。
    • go mod why:查看为什么需要依赖某模块。
  • go work:跨文件目录操作本地包、多个mod模块包调用、本地测试。
  • go run:可以编译并运行命令源码文件,并把编译后的可执行文件存放到临时工作目录。常用
  • go test:用于以代码包为单位对Go语言编写的程序进行测试。常用
  • go tool:执行go自带的工具。go tool pprof对cpu、内存和协程进行监控;go tool trace跟踪协程的执行过程。
  • go version:打印 Go 可执⾏⽂件的构建信息。
  • go vet:用于检查Go语言源码中静态错误的简单工具。

标准库

Go 标准库 https://golang.google.cn/pkg/

精心策划的Go项目列表 https://github.com/golang/go/wiki/Projects

目前官方提供190多种标准库包(包括cmd包),涵盖大部分的使用场景,如压缩、加密、数据库、编码、异常、画图、打印、IO操作、网络、操作系统、反射HTML模版等,可以先大概看下官方每个包功能大体说明,需要使用某些功能细节时再具体查看官网。相对于标准库,其他包也称为子库,这些包也是Go项目的一部分,但在Go主干之外。它们是在比Go内核更宽松的兼容性要求下开发的。可以用“go get”安装它们。

基础语法

Effective Go 概览

Go是一门新语言,尽管它借鉴了现有语言的思想,但它具有不同寻常的特性,使有效的Go程序与用其相关语言编写的程序在性质上有所不同。直接将c++或Java程序翻译成Go不太可能产生令人满意的结果。重要的是要先了解它的性质和习惯用法,了解用Go编程的既定约定比如命名、格式、程序结构等等。比如拿分号(Semicolons)来说,像C一样,Go的形式语法使用分号来终止语句,但与C不同的是,这些分号不会出现在源代码中。相反,词法分析器使用一个简单的规则在扫描时自动插入分号,因此输入文本基本上没有分号,也即是在Go语言中不需要采用分号结尾。

云原生时代崛起的编程语言Go基础实战

命名规范

Go语言核心宗旨就是简洁,在Go语言的相关命名也是推荐如此,命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名,从语法层面进行了以下限定;任何需要对外暴露的名字必须以大写字母开头类似public,不需要对外暴露的则应该以小写字母开头类似private。

  • 一般命名:推荐驼峰命名方式,如userManger或UserManager;单词缩写默认全大写或全小写,如userID、baiduCDN、id、cdn。
  • 项目名:小写,多个单词建议采用中划线分隔,比如github.com/gin-gonic。
  • 包名:package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。包名应该为小写单词,不要使用下划线或者混合大小写,统一使用单数形式。如domain、main。
  • 文件名:文件命名一律采用小写,见名思义,测试文件以test.go结尾,如stringutil.go, stringutil_test.go。
  • Local变量:保持简短;如索引采用i而不采用index,reader简写r,buffer简写为b;避免冗余,命名不要和上下文重叠,在方法体内变量名count 会比runeCount更简洁,在map的上下文下可以简写:v,ok=m[k]。
func RuneCount(b []byte) int {
    count := 0
    for i := 0; i < len(b); {
        if b[i] < RuneSelf {
            i++
        } else {
            _, n := DecodeRune(b[i:])
            i += n
        }
        count++
    }
    return count
}
func Read(b *Buffer, p []byte) (n int, err error) {
        if b.empty() {
                b.Reset()
        }
        n = copy(p, b.buf[b.off:])
        b.off += n
        return n, nil
}
  • 结构体:名词或名词短语,如Account、Book,避免使用Manager这样的。 如果该数据结构需要序列化,如json, 则首字母大写, 包括里面的字段。
type Host struct {
    Port string `json:"port"`
    Address string `json:"address"`
}
  • 接口:接口单个函数的接口名以 er 为后缀,两个函数的接口名可以综合两个函数名,三个以上函数的接口名类似于结构体名。
type Reader interface {
    Read(p []byte) (n int, err error)
}
type WriteFlusher interface {
    Write([]byte) (int, error)
    Flush() error
}
type Car interface {
    Start() 
    Stop()
    Drive()
}
  • 方法:动词或动词短语;如果是结构体方法,那么 Receiver 的名称应该缩写, 要么都使用值, 要么都用指针;一般用一个或两个字母(优先r);对于Receiver命名应该统一, 要么都使用值, 要么都用指针,如果 Receiver 是指针可统一使用p。
func (b *Buffer) Read(p []byte) (n int, err error){
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request){
}
func (r Rectangle) Size() Point{
}
func (f foo) method() {
}
func (p *foo) method() {
}

Go没对get/set方法特别支持,必要的时候可以自己定义,Go对get有不同建议,如

// 推荐
p.FirstName()
// 不推荐
p.GetFirstName()
  • error:自定义error命名通常以 “名称+Error” 作为结构体的名字,变量时会用简写err + 名称。
type TypeError struct {
	Errors []string
}
ErrShortDst = errors.New("transform: short destination buffer")
  • 常用缩写
src = source
srv = server
arg = argument
conn = connect, connection
attr = attribute
abs = absolute
min = minimum
len = length
auth = authenticate
buf = buffer
ctl = control
ctx = context
str = string
msg = message
fmt = format
dest = destination
diff = difference
orig = original
recv = receive
ref = reference
repo = repository
util = utility

注释

Go提供C风格的/* */块注释和c++风格的//行注释。行注释是常态;块注释主要作为包注释出现,但在表达式中或禁用大量代码时很有用。

变量

在Go语言中,变量被显式声明并被编译器用来检查函数调用的类型正确性;var声明1个或多个变量。

package main

import "fmt"

func main() {

    var a = "initial"
    fmt.Println(a)

    var b, c int = 1, 2
    fmt.Println(b, c)

    var d = true
    fmt.Println(d)

    var e int
    fmt.Println(e)

    f := "apple"
    fmt.Println(f)
}

云原生时代崛起的编程语言Go基础实战

常量(const)

Go支持字符常量、字符串常量、布尔常量和数值常量;const声明一个常数值。

package main
import (
    "fmt"
    "math"
)
const s string = "constant"
func main() {
    fmt.Println(s)
    const n = 500000000
    const d = 3e20 / n
    fmt.Println(d)
    fmt.Println(int64(d))
    fmt.Println(math.Sin(n))
}

云原生时代崛起的编程语言Go基础实战

控制结构

  • 循环(For):for是Go唯一的循环结构。
package main

import "fmt"

func main() {

    i := 1
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }

    for j := 7; j <= 9; j++ {
        fmt.Println(j)
    }

    for {
        fmt.Println("loop")
        break
    }

    for n := 0; n <= 5; n++ {
        if n%2 == 0 {
            continue
        }
        fmt.Println(n)
    }
}

云原生时代崛起的编程语言Go基础实战

  • 选择(If/Else):在Go中没有三元if,所以即使对于基本条件,也需要使用完整的if语句。
package main

import "fmt"

func main() {

    if 7%2 == 0 {
        fmt.Println("7 is even")
    } else {
        fmt.Println("7 is odd")
    }

    if 8%4 == 0 {
        fmt.Println("8 is divisible by 4")
    }

    if num := 9; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has 1 digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }
}

云原生时代崛起的编程语言Go基础实战

  • Switch:跨多个分支表达条件。
package main

import (
    "fmt"
    "time"
)

func main() {

    i := 2
    fmt.Print("Write ", i, " as ")
    switch i {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    }

    switch time.Now().Weekday() {
    case time.Saturday, time.Sunday:
        fmt.Println("It's the weekend")
    default:
        fmt.Println("It's a weekday")
    }

    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("It's before noon")
    default:
        fmt.Println("It's after noon")
    }

    whatAmI := func(i interface{}) {
        switch t := i.(type) {
        case bool:
            fmt.Println("I'm a bool")
        case int:
            fmt.Println("I'm an int")
        default:
            fmt.Printf("Don't know type %T\n", t)
        }
    }
    whatAmI(true)
    whatAmI(1)
    whatAmI("hey")
}

云原生时代崛起的编程语言Go基础实战

数据类型

  • 数组(Arrays):数组是特定长度的元素的编号序列。单在典型的Go代码中,切片更为常见,后面介绍。
package main

import "fmt"

func main() {

    var a [5]int
    fmt.Println("emp:", a)

    a[4] = 100
    fmt.Println("set:", a)
    fmt.Println("get:", a[4])

    fmt.Println("len:", len(a))

    b := [5]int{1, 2, 3, 4, 5}
    fmt.Println("dcl:", b)

    var twoD [2][3]int
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}

云原生时代崛起的编程语言Go基础实战

  • 切片(Slices):是Go中的一种重要数据类型,它为序列提供了比数组更强大的接口。
package main

import "fmt"

func main() {

    s := make([]string, 3)
    fmt.Println("emp:", s)

    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("set:", s)
    fmt.Println("get:", s[2])

    fmt.Println("len:", len(s))

    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println("apd:", s)

    c := make([]string, len(s))
    copy(c, s)
    fmt.Println("cpy:", c)

    l := s[2:5]
    fmt.Println("sl1:", l)

    l = s[:5]
    fmt.Println("sl2:", l)

    l = s[2:]
    fmt.Println("sl3:", l)

    t := []string{"g", "h", "i"}
    fmt.Println("dcl:", t)

    twoD := make([][]int, 3)
    for i := 0; i < 3; i++ {
        innerLen := i + 1
        twoD[i] = make([]int, innerLen)
        for j := 0; j < innerLen; j++ {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}

云原生时代崛起的编程语言Go基础实战

  • 映射(Maps):Go内置的关联数据类型,在其他语言中有时称为哈希或字典。
package main

import "fmt"

func main() {

    m := make(map[string]int)

    m["k1"] = 7
    m["k2"] = 13

    fmt.Println("map:", m)

    v1 := m["k1"]
    fmt.Println("v1:", v1)

    v3 := m["k3"]
    fmt.Println("v3:", v3)

    fmt.Println("len:", len(m))

    delete(m, "k2")
    fmt.Println("map:", m)

    _, prs := m["k2"]
    fmt.Println("prs:", prs)

    n := map[string]int{"foo": 1, "bar": 2}
    fmt.Println("map:", n)
}

云原生时代崛起的编程语言Go基础实战

迭代(range)

Range遍历各种数据结构中的元素

package main

import "fmt"

func main() {

    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)

    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }

    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }

    for k := range kvs {
        fmt.Println("key:", k)
    }

    for i, c := range "go" {
        fmt.Println(i, c)
    }
}

云原生时代崛起的编程语言Go基础实战

函数

Go函数返回值需要显式返回,即它不会自动返回最后一个表达式的值。有多个相同类型的连续参数时可以省略类型相似的参数的类型名称,直到声明该类型的最后一个参数。

package main

import "fmt"

// 多个参数
func plus(a int, b int) int {

	return a + b
}
// 多个相同类型参数
func plusPlus(a, b, c int) int {
	return a + b + c
}
// 多个返回值
func vals() (int, int) {
	return 3, 7
}
// 可变参数
func sum(nums ...int) {
    fmt.Print(nums, " ")
    total := 0

    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}
// 闭包,Go支持匿名函数,它可以形成闭包。当想要内联定义一个函数而不必命名它时可以使用匿名函数很。
func intSeq() func() int {
    i := 0
    return func() int {
        i++
        return i
    }
}
// 递归函数
func fact(n int) int {
    if n == 0 {
        return 1
    }
    return n * fact(n-1)
}

func main() {

	res := plus(1, 2)
	fmt.Println("1+2 =", res)

	res = plusPlus(1, 2, 3)
	fmt.Println("1+2+3 =", res)
    
    a, b := vals()
	fmt.Println(a)
	fmt.Println(b)

	_, c := vals()
	fmt.Println(c)
    
    sum(1, 2)
    sum(1, 2, 3)

    nums := []int{1, 2, 3, 4}
    sum(nums...)
    
    nextInt := intSeq()

    fmt.Println(nextInt())
    fmt.Println(nextInt())
    fmt.Println(nextInt())

    newInts := intSeq()
    fmt.Println(newInts())
    
    fmt.Println(fact(7))

    var fib func(n int) int

    fib = func(n int) int {
        if n < 2 {
            return n
        }

        return fib(n-1) + fib(n-2)
    }

    fmt.Println(fib(7))
}

云原生时代崛起的编程语言Go基础实战

指针

Go支持指针,允许在程序中传递对值和记录的引用。

package main

import "fmt"

func zeroval(ival int) {
    ival = 0
}

func zeroptr(iptr *int) {
    *iptr = 0
}

func main() {
    i := 1
    fmt.Println("initial:", i)

    zeroval(i)
    fmt.Println("zeroval:", i)

    zeroptr(&i)
    fmt.Println("zeroptr:", i)

    fmt.Println("pointer:", &i)
}

字符串和符文

Go字符串是字节的只读切片。该语言和标准库将字符串特殊地视为UTF-8编码文本的容器。在其他语言中,字符串是由“字符”组成的。在go中,字符的概念被称为符文——它是一个表示Unicode码点的整数。

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {

    const s = "สวัสดี"

    fmt.Println("Len:", len(s))

    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
    fmt.Println()

    fmt.Println("Rune count:", utf8.RuneCountInString(s))

    for idx, runeValue := range s {
        fmt.Printf("%#U starts at %d\n", runeValue, idx)
    }

    fmt.Println("\nUsing DecodeRuneInString")
    for i, w := 0, 0; i < len(s); i += w {
        runeValue, width := utf8.DecodeRuneInString(s[i:])
        fmt.Printf("%#U starts at %d\n", runeValue, i)
        w = width

        examineRune(runeValue)
    }
}

func examineRune(r rune) {

    if r == 't' {
        fmt.Println("found tee")
    } else if r == 'ส' {
        fmt.Println("found so sua")
    }
}

云原生时代崛起的编程语言Go基础实战

结构体(struct)

Go的结构体是字段的类型化集合,将数据分组形成记录;结构体是可变的。

package main

import "fmt"

type person struct {
    name string
    age  int
}

func newPerson(name string) *person {

    p := person{name: name}
    p.age = 42
    return &p
}

func main() {

    fmt.Println(person{"Bob", 20})

    fmt.Println(person{name: "Alice", age: 30})

    fmt.Println(person{name: "Fred"})

    fmt.Println(&person{name: "Ann", age: 40})

    fmt.Println(newPerson("Jon"))

    s := person{name: "Sean", age: 50}
    fmt.Println(s.name)

    sp := &s
    fmt.Println(sp.age)

    sp.age = 51
    fmt.Println(sp.age)
}

云原生时代崛起的编程语言Go基础实战

方法

Go支持在结构类型上定义的方法。方法的接受者可以是结构体也可以是指向结构体的指针。

package main

import "fmt"

type rect struct {
	width, height int
}

func (r *rect) area() int {
	return r.width * r.height
}

func (r rect) perim() int {
	return 2*r.width + 2*r.height
}

func main() {
	r := rect{width: 10, height: 5}

	fmt.Println("area: ", r.area())
	fmt.Println("perim:", r.perim())

	rp := &r
	fmt.Println("area: ", rp.area())
	fmt.Println("perim:", rp.perim())
}

云原生时代崛起的编程语言Go基础实战

接口(interface)

接口是方法签名的命名集合。

package main

import (
    "fmt"
    "math"
)

type geometry interface {
    area() float64
    perim() float64
}

type rect struct {
    width, height float64
}
type circle struct {
    radius float64
}

func (r rect) area() float64 {
    return r.width * r.height
}
func (r rect) perim() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perim())
}

func main() {
    r := rect{width: 3, height: 4}
    c := circle{radius: 5}

    measure(r)
    measure(c)
}

云原生时代崛起的编程语言Go基础实战

Go支持嵌入结构体和接口,以表达更丰富的类型组合。

package main

import "fmt"

type base struct {
    num int
}

func (b base) describe() string {
    return fmt.Sprintf("base with num=%v", b.num)
}

type container struct {
    base
    str string
}

func main() {

    co := container{
        base: base{
            num: 1,
        },
        str: "some name",
    }

    fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)

    fmt.Println("also num:", co.base.num)

    fmt.Println("describe:", co.describe())

    type describer interface {
        describe() string
    }

    var d describer = co
    fmt.Println("describer:", d.describe())
}

云原生时代崛起的编程语言Go基础实战

泛型

Go 1.18 引入了泛型,为 Go 语言带来了更强大的类型系统,使其可以更好地支持各种数据类型和算法。Go 泛型可以应用于各种数据类型和算法,使代码更加通用、简洁、易读和易维护。

package main

import "fmt"

func MapKeys[K comparable, V any](m map[K]V) []K {
    r := make([]K, 0, len(m))
    for k := range m {
        r = append(r, k)
    }
    return r
}

type List[T any] struct {
    head, tail *element[T]
}

type element[T any] struct {
    next *element[T]
    val  T
}

func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}

func (lst *List[T]) GetAll() []T {
    var elems []T
    for e := lst.head; e != nil; e = e.next {
        elems = append(elems, e.val)
    }
    return elems
}

func main() {
    var m = map[int]string{1: "2", 2: "4", 4: "8"}

    fmt.Println("keys:", MapKeys(m))

    _ = MapKeys[int, string](m)

    lst := List[int]{}
    lst.Push(10)
    lst.Push(13)
    lst.Push(23)
    fmt.Println("list:", lst.GetAll())
}

云原生时代崛起的编程语言Go基础实战

以下是一些使用 Go 泛型的场景:

  • 集合数据类型:Go 泛型可以方便地定义各种集合数据类型,如栈、队列、链表、二叉树等。下面使用泛型定义栈的例子定义了一个 Stack[T] 类型,表示一个可以存储任意类型 T 的栈。Push 方法用于将一个元素压入栈中,Pop 方法用于弹出栈顶的元素。
  • 算法实现:泛型还可以用于实现各种算法,如排序、查找、字符串匹配等。以下是一个使用泛型实现快速排序的例子:在这个例子中,使用泛型定义 QuickSort 函数,可以对任意类型 T 的切片进行排序。该函数采用经典的快速排序算法实现。

错误(errors)

在Go中,习惯上通过显式的单独返回值来传达错误。这与Java和Ruby等语言中使用的异常以及c中有时使用的重载的单个结果/错误值形成对比。Go的方法可以很容易地看到哪些函数返回错误,并使用用于任何其他无错误任务的相同语言结构来处理它们。

package main

import (
    "errors"
    "fmt"
)

func f1(arg int) (int, error) {
    if arg == 42 {

        return -1, errors.New("can't work with 42")

    }

    return arg + 3, nil
}

type argError struct {
    arg  int
    prob string
}

func (e *argError) Error() string {
    return fmt.Sprintf("%d - %s", e.arg, e.prob)
}

func f2(arg int) (int, error) {
    if arg == 42 {

        return -1, &argError{arg, "can't work with it"}
    }
    return arg + 3, nil
}

func main() {

    for _, i := range []int{7, 42} {
        if r, e := f1(i); e != nil {
            fmt.Println("f1 failed:", e)
        } else {
            fmt.Println("f1 worked:", r)
        }
    }
    for _, i := range []int{7, 42} {
        if r, e := f2(i); e != nil {
            fmt.Println("f2 failed:", e)
        } else {
            fmt.Println("f2 worked:", r)
        }
    }

    _, e := f2(42)
    if ae, ok := e.(*argError); ok {
        fmt.Println(ae.arg)
        fmt.Println(ae.prob)
    }
}

云原生时代崛起的编程语言Go基础实战

恐慌(pinic)

恐慌通常意味着事情出乎意料地出错了;大多数情况下,使用它来快速处理在正常操作中不应该发生的错误,或者没有准备好优雅地处理的错误。

package main

import "os"

func main() {

    panic("a problem")

    _, err := os.Create("/tmp/file")
    if err != nil {
        panic(err)
    }
}

云原生时代崛起的编程语言Go基础实战

推迟(defer)

Defer用于确保函数调用在程序执行的后期执行,通常是出于清理目的。Defer通常用于其他语言中使用的地方,例如ensure和finally。比如用于释放或关闭连接、释放或关闭文件句柄等

package main

import (
    "fmt"
    "os"
)

func main() {

    f := createFile("d:/tmp/defer.txt")
    defer closeFile(f)
    writeFile(f)
}

func createFile(p string) *os.File {
    fmt.Println("creating")
    f, err := os.Create(p)
    if err != nil {
        panic(err)
    }
    return f
}

func writeFile(f *os.File) {
    fmt.Println("writing")
    fmt.Fprintln(f, "data")

}

func closeFile(f *os.File) {
    fmt.Println("closing")
    err := f.Close()

    if err != nil {
        fmt.Fprintf(os.Stderr, "error: %v\n", err)
        os.Exit(1)
    }
}

云原生时代崛起的编程语言Go基础实战

恢复(recover)

通过使用内置的recover函数,Go使从恐慌中恢复成为可能。恢复可以阻止因恐慌而中止程序,而是让程序继续执行。

package main

import "fmt"

func mayPanic() {
    panic("a problem")
}

func main() {

    defer func() {
        if r := recover(); r != nil {

            fmt.Println("Recovered. Error:\n", r)
        }
    }()

    mayPanic()

    fmt.Println("After mayPanic()")
}

云原生时代崛起的编程语言Go基础实战文章来源地址https://www.toymoban.com/news/detail-428518.html

  • 本人博客网站IT小神 www.itxiaoshen.com

到了这里,关于云原生时代崛起的编程语言Go基础实战的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Go】四、rpc跨语言编程基础与rpc的调用基础原理

    早期 Go 语言不使用 go module 进行包管理,而是使用 go path 进行包管理,这种管理方式十分老旧,两者最显著的区别就是:Go Path 创建之后没有 go.mod 文件被创建出来,而 go module 模式会创建出一个 go.mod 文件用于管理包信息 现在就是:尽量使用 Go Modules 模式 另外,我们在引入包

    2024年02月19日
    浏览(37)
  • 【Go 基础篇】Go语言关键字和预定义标识符解析:探索编程的基石与核心要素

    在计算机编程中,(Keywords)和预定义标识符(Predefined Identifiers)是编程语言的核心要素,它们在语法结构和语言功能中起到重要作用。在Go语言(Golang)中,和预定义标识符定义了编程的基本规则和构建块,是实现功能的关键。本篇博客将深入探讨Go语言中的关

    2024年02月12日
    浏览(64)
  • 100天精通Golang(基础入门篇)——第15天:深入解析Go语言中函数的应用:从基础到进阶,助您精通函数编程!(进阶)

    🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批

    2024年02月12日
    浏览(66)
  • GO编程语言:简洁、高效、强大的开源编程语言

    在现代软件开发领域,随着应用复杂度的不断提升,开发人员对编程语言的需求也日益增长。GO编程语言,作为一种简洁、高效且具备强大并发能力的新型开源编程语言,逐渐成为了许多开发者的首选。本文将详细介绍GO语言在哪些项目开发中表现出色,以及为什么许多开发者

    2024年02月02日
    浏览(96)
  • Go语言网络编程(socket编程)WebSocket编程

    WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据 在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输 需要安装第

    2024年02月09日
    浏览(71)
  • Go语言网络编程(socket编程)http编程

    Web服务器的工作原理可以简单地归纳为 客户机通过TCP/IP协议建立到服务器的TCP连接 客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档 服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“

    2024年02月09日
    浏览(65)
  • GO语言网络编程(并发编程)Channel

    1.1.1 Channel 单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。 虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势

    2024年02月09日
    浏览(64)
  • GO语言网络编程(并发编程)select

    1.1.1 select多路复用 在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现: 这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,G

    2024年02月09日
    浏览(75)
  • 【Go编程语言】流程控制

    程序的流程控制结构一具有三种:顺序结构,选择结构,循环结构 顺序结构:从上到下,逐行执行。默认的逻辑 选择结构:条件满足某些代码才会执行 if switch select ,后面 channel 再讲 循环结构:条件满足某些代码会被反复执行0-N次 for 条件语句需要开发者通过指定一个或多个条

    2024年02月06日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包