3368 字
17 分钟
Golang
2025-02-16

下载#

All releases - The Go Programming Language

my)Go!!!!!#

基础#

结构#

package main
import "fmt"
func main() {
   /* 注释 */
   fmt.Println("Hello, World!")
}

和python差不多,你可以格式化输出

func main() {
    var hello="Hello%s"
    var name="Rxiain"
    fmt.Printf(heello,name)
}

变量名不能以数字开头,声明变量时你可以指定类型,belike var hello string,没有指定内容(初始化)则数字默认为值 0,布尔值为 False,字符串为空,其它类型为 nil(不是null,是nil

你可以因式分解表达一些东西使其看起来更规整

var (
    v1 e1
    v2 e2
)

import (
    "fmt"
)

初始化声明#

新东西,上例中的 var hello="Hello%s"可以简化为 hello := "Hello%s",而这时相同的,所以用过 var后再使用 :=符号则会报错

引用&指针#

和cpp一样,可以使用 &i获得 i的内存地址,也可以var一个指针存储指定变量的内存地址,两个变量的赋值操作也是让其指向的内存地址相同。

func main() {
    var c = 1
    var d = 2
    var a = &c
    var b = &d
    fmt.Println("a:%p, b:%p", a, b)
    a = b
    fmt.Println("a:%p, b:%p", a, b)
}

指针声明:var var_name *var-type belike:var ip *int 然后可以通过 *ip访问ip指向的内存里包含的东西

常量#

const :与 var差不多的使用形式

iota#

iota,特殊常量,可以认为是一个可以被编译器修改的常量。 iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

func main() {
    const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //独立值,iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7,恢复计数
            i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)
}

插一嘴,go下的类型转换与cpp的结构不同,belike:int(something) go并不支持隐式类型转换

条件判断#

  1. 简单的if:
   if a < 0 {   
       fmt.Printf("a<0" )  
   } else if{
   ...
   } else {
   ...
   }
  1. switch和cpp差不多
  2. select语句,这个与Go的通道、goroutlines有关,放在后面

函数#

func function_name( [parameter list] ) [return_types] {
   函数体
}

可以看到返回类型放置在了后面 go的函数可以返回多个值

数组#

声明:var arrayName [size]dataType 初始化:var numbers = [5]int{1, 2, 3:6.0, 4, 5}

注意go的数组大小是类型的一部分,不同的大小下即使是相同的类型也不相互兼容 大小可以用 ...表示,编译器会根据元素自行判断大小 不填大小,编译器会根据 {}里的元素推断大小,也就是之后的元素 在 {}里初始化元素可以指定下标

结构体#

定义belike:

type Books struct {  
   title string  
   author string  
   subject string  
   book_id int  
}

声明类型 var a Books此时可以通过 a.title调用结构体中的数据 另外,指针也可以存储结构体的变量地址

切片#

动态数组,可以追加内容,声明方法与数组区别上就是不填大小,切片的使用比数组更为常见。 声明:var identifier []type belike:slice1 := make([]type, len, cap) 其中len时原始长度,cap是最大容量

初始化:

  • s :=[] int {1,2,3 }
  • s := arr[:]引用数列arr
  • s := arr[startIndex:endIndex]跟上面的差不多,只不过指定了下标范围
  • s1 := s[startIndex:endIndex]也可以用存在的切片来初始化

切片是可索引的,并且可以由 len() 方法获取长度。 切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

追加新元素append()函数,belike:numbers = append(numbers, 1),其中numbers是是一个切片

拷贝切片: copy函数,belike:copy(numbers1,numbers),要注意 numbers1的容量要大于等于 numbers

Map#

无序的键值对集合 定义:map_variable := make(map[KeyType]ValueType, initialCapacity),需要指明键值的数据类型 也可以:

m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}

用delete函数删除元素:delete(Map的变量,Key)

接口#

接口(interface)是 Go 语言中的一种类型,用于定义行为的集合,它通过描述类型必须实现的方法,规定了类型的行为契约。

任意类型只要实现了所有方法,就被认为实现了该接口

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/
}

例子:

package main
import (
    "fmt"
)

type DataUtils interface {
    Get() string
}

type Person struct{
    Name string
    Age int
    Id int
}

func (a Person) Get() string {
    return fmt.Sprintf("Name: %s, Age: %d, Id: %d", a.Name, a.Age, a.Id)
}

func main() {
    p := Person{
        Name: "orxiain",
        Age: 20,
        Id: 1,
    }
    fmt.Println(p.Get())
}

空接口#

用来实现泛型数据处理

package main

import "fmt"

func printValue(val interface{}) {
        fmt.Printf("Value: %v, Type: %T\n", val, val)
}

func main() {
        printValue(42)         // int
        printValue("hello")    // string
        printValue(3.14)       // float64
        printValue([]int{1, 2}) // slice
}

类型断言#

类型断言(Type Assertion)是一种用于将接口类型的值转换为具体类型的操作

value, ok := interfaceValue.(Type)
  • interfaceValue 是一个接口类型的变量。
  • Type 是你想要断言的目标类型。
  • value 是断言后的具体类型的值(如果断言成功)。
  • ok 是一个布尔值,表示断言是否成功.

这样用:

func main (){
    var i interface{} = "Hello"
    if s, ok := i.(string); ok {
        fmt.Println("Value is a string:", s)
    } else {
        fmt.Println("Value is not a string")
    }
}

搭配 switch实现不同数据类型的不同操作

func printType(val interface{}) {  
        switch v := val.(type) {  
        case int:  
                fmt.Println("Integer:", v)  
        case string:  
                fmt.Println("String:", v)  
        case float64:  
                fmt.Println("Float:", v)  
        default:  
                fmt.Println("Unknown type")  
        }  
}

错误#

定义错误并使用

package main  
  
import (  
    "errors"  
    "fmt"  
)  
  
func main() {  
    err := errors.New("this is an error")  
    fmt.Println(err) // 输出:this is an error  
}

fmt.ErrrorF fmt.Errorf 是一个用于创建格式化错误消息的函数。它属于 fmt 包,可以接受一个格式化字符串和一系列参数,返回一个实现了 error 接口的对象。1.13版本之后可以用 %w占位符包装错误

originalErr := fmt.Errorf("原始错误:文件未找到")
wrappedErr := fmt.Errorf("包装错误:%w", originalErr)

errors.Is#

在 Go 语言中,errors.Is 函数用于判断一个错误链中是否包含某个特定的错误。它通常用于检查一个被包装过的错误是否包含某个基础错误

基本用法:

func Is(err, target error) bool

其中 err是被检查的错误,target error是检测的目标错误

  • 如果 target 为 nil,则直接比较 err 是否也为 nil.
  • 如果 err 和 target 都是可比较的,并且它们相等,则返回 true.
  • 如果 err 实现了 Is 方法,并且调用 err.Is(target) 返回 true,则返回 true.
  • 否则,递归调用 Unwrap 方法来获取 err 的下一层错误,并继续检查

使用例(什么绿幕素材靶场

package main
import (
    "fmt"
    "errors"
)

var BaseErr = errors.New("the underlying base error")

func main() {
    err1 := fmt.Errorf("wrap base: %w", BaseErr)
    err2 := fmt.Errorf("wrap err1: %w", err1)//这里err2包含了err1,err1包含了BaseErr
  
    if errors.Is(err2, BaseErr) {
        fmt.Println("err2 contains BaseErr")
    } else {
        fmt.Println("err2 does not contain BaseErr")
    }
}

errors.As#

errors.As 函数用于从一个错误链中提取指定类型的错误。它的作用是判断包装的错误链中是否存在某个特定类型的错误,并将第一个符合该类型的错误赋值给目标变量

func As(err error, target any) bool

err 是需要检查的错误;target 是一个指向目标类型的指针,用于接收匹配到的错误。

  • errors.As 会递归地检查 err 错误链中的每一层错误.
  • 如果找到一个错误的类型与 target 的类型匹配,则将该错误赋值给 target 并返回 true.
  • 如果 err 实现了 As 方法,则调用该方法进行处理 使用例
package main
import (
    "errors"
    "fmt"
)

type TypicalErr struct {
    e string
}

func (t TypicalErr) Error() string {
    return t.e
}

func main() {
    err := TypicalErr{"typical error"}
    err1 := fmt.Errorf("wrap err: %w", err)
    err2 := fmt.Errorf("wrap err1: %w", err1)
  
    var e TypicalErr
    if errors.As(err2, &e) {
        fmt.Println("TypicalErr is on the chain of err2")
        fmt.Println(err == e) // true
    } else {
        fmt.Println("TypicalErr is not on the chain of err2")
    }
}

并发#

Goroutines#

  • Go 中的并发执行单位,类似于轻量级的线程。
  • Goroutine 的调度由 Go 运行时管理,用户无需手动分配线程。
  • 使用 go 关键字启动 Goroutine。
  • Goroutine 是非阻塞的,可以高效地运行成千上万个 Goroutine。 创建一个Goroutines的语法:
go myFunction()

channels#

  • Go 中用于在 Goroutine 之间通信的机制。
  • 支持同步和数据共享,避免了显式的锁机制。
  • 使用 chan 关键字创建,通过 <- 操作符发送和接收数据。没有 ->只有 <-

使用例

package main

import (
    "fmt"
)

func main() {
    // 创建一个无缓冲的 Channel,类型为 int
    ch := make(chan int)
  
    // 开启一个 goroutine,将数据发送到 Channel 中
    go func() {
        ch <- 42 // 发送数据
    }()
  
    // 从 Channel 中接收数据
    value := <-ch
    fmt.Println("Received:", value)
}

创建Channel时可以指定缓冲区大小:

ch := make(chan int,100)

缓冲用例

package main

import "fmt"

func main() {
    // 这里我们定义了一个可以存储整数类型的带缓冲通道
    // 缓冲区大小为2
    ch := make(chan int, 2)

    // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
    // 而不用立刻需要去同步读取数据
    ch <- 1
    ch <- 2

    // 获取这两个数据
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

select#

select 语句使得一个 goroutine 可以等待多个通信操作。select 会阻塞,直到其中的某个 case 可以继续执行:

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)

    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

WaitGroup#

sync.WaitGroup 是 Go 语言 sync 包中提供的一个同步原语,用于等待一组并发操作的完成。它通常用于确保在所有启动的 Goroutine 完成它们的任务之前,主 Goroutine 不会提前退出。WaitGroup 通过计数器来实现这一功能,计数器的值表示需要等待的 Goroutine 数量。

主要方法:

  • Add(delta int):将计数器的值增加 delta。通常在启动 Goroutine 之前调用,传入的 delta 为 Goroutine 的数量.
  • Done():将计数器的值减少 1。通常在 Goroutine 完成其任务后调用.
  • Wait():阻塞调用者,直到计数器的值为 0。通常在主 Goroutine 中调用,用于等待所有 Goroutine 完成.

用例:

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 确保在函数结束时调用 Done
  
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second) // 模拟耗时操作
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
  
    // 启动 3 个 Goroutine
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 每启动一个 Goroutine,计数器加 1
        go worker(i, &wg)
    }
  
    // 等待所有 Goroutine 完成
    wg.Wait()
    fmt.Println("All workers done")
}

Gin#

Gin Web Framework

什么是Gin? Gin是一个使用Go语言开发的Web框架。 它提供类似Martini的API,但性能更佳,速度提升高达40倍。 如果你是性能和高效的追求者, 你会爱上 Gin。

截止25.1.8 Gin已获得79.8k star,是目前最流行的Golang web框架

安装#

mkdir一个你要干大事的文件夹并cd进去 新建一个go模块

go mod init gin

安装框架

go get github.com/gin-gonic/gin

一下是官方文档给出的示例项目,将它保存到 main.go

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

var db = make(map[string]string)

func setupRouter() *gin.Engine {
	// Disable Console Color
	// gin.DisableConsoleColor()
	r := gin.Default()

	// Ping test
	r.GET("/ping", func(c *gin.Context) {
		c.String(http.StatusOK, "pong")
	})

	// Get user value
	r.GET("/user/:name", func(c *gin.Context) {
		user := c.Params.ByName("name")
		value, ok := db[user]
		if ok {
			c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
		} else {
			c.JSON(http.StatusOK, gin.H{"user": user, "status": "no value"})
		}
	})

	// Authorized group (uses gin.BasicAuth() middleware)
	// Same than:
	// authorized := r.Group("/")
	// authorized.Use(gin.BasicAuth(gin.Credentials{
	//	  "foo":  "bar",
	//	  "manu": "123",
	//}))
	authorized := r.Group("/", gin.BasicAuth(gin.Accounts{
		"foo":  "bar", // user:foo password:bar
		"manu": "123", // user:manu password:123
	}))

	/* example curl for /admin with basicauth header
	   Zm9vOmJhcg== is base64("foo:bar")

		curl -X POST \
	  	http://localhost:8080/admin \
	  	-H 'authorization: Basic Zm9vOmJhcg==' \
	  	-H 'content-type: application/json' \
	  	-d '{"value":"bar"}'
	*/
	authorized.POST("admin", func(c *gin.Context) {
		user := c.MustGet(gin.AuthUserKey).(string)

		// Parse JSON
		var json struct {
			Value string `json:"value" binding:"required"`
		}

		if c.Bind(&json) == nil {
			db[user] = json.Value
			c.JSON(http.StatusOK, gin.H{"status": "ok"})
		}
	})

	return r
}

func main() {
	r := setupRouter()
	// Listen and Server in 0.0.0.0:8080
	r.Run(":8080")
}

运行 go run main.go即可在访问 ~~好了你已经知道怎么写网页了,接下来做个后端8( ̄︶ ̄)↗ ~~ 官方文档提供了丰富的功能实例 示例 | Gin Web Framework

之后就用goland开发了

#

数字数据类型

序号类型和描述
1uint8 无符号 8 位整型 (0 到 255)
2uint16 无符号 16 位整型 (0 到 65535)
3uint32 无符号 32 位整型 (0 到 4294967295)
4uint64 无符号 64 位整型 (0 到 18446744073709551615)
5int8 有符号 8 位整型 (-128 到 127)
6int16 有符号 16 位整型 (-32768 到 32767)
7int32 有符号 32 位整型 (-2147483648 到 2147483647)
8int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

工具 & 高质量文

Golang
https://fuwari.vercel.app/posts/golang/
作者
𝚘𝚛𝚡𝚒𝚊𝚒𝚗.
发布于
2025-02-16
许可协议
CC BY-NC-SA 4.0