go学习笔记——基本语法

1.*和&的区别

& 是取地址符号 , 即取得某个变量的地址 , 如 &a

* 是指针运算符 , 可以表示一个变量是指针类型 , 也可以表示一个指针变量所指向的存储单元 , 也就是这个地址所存储的值

参考:Go中*和&区别

println打印对象只能打印出其指针,需要使用fmt.Printf,如下

fmt.Printf("%+v\n", user)

参考:Golang 通过fmt包输出完整struct信息

2.defer

defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行。

defer语句通常用于一些成对操作的场景:打开连接/关闭连接;加锁/释放锁;打开文件/关闭文件等。

defer在一些需要回收资源的场景非常有用,可以很方便地在函数结束前做一些清理操作。在打开资源语句的下一行,直接一句defer就可以在函数返回前关闭资源,可谓相当优雅。

f, _ := os.Open("defer.txt")
defer f.Close()

参考:Golang之轻松化解defer的温柔陷阱

3.日志库

1.sirupsen/logrus

go get -u github.com/sirupsen/logrus

文档

https://pkg.go.dev/github.com/sirupsen/logrus#section-readme

使用

package main

import (
  "os"
  "github.com/sirupsen/logrus"
)

// Create a new instance of the logger. You can have any number of instances.
var log = logrus.New()

func main() {
  // The API for setting attributes is a little different than the package level
  // exported logger. See Godoc.
  log.Out = os.Stdout

  // You could set this to any `io.Writer` such as a file
  // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
  // if err == nil {
  //  log.Out = file
  // } else {
  //  log.Info("Failed to log to file, using default stderr")
  // }

  log.WithFields(logrus.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")
}

2.rs/zerolog

go get -u github.com/rs/zerolog/log

使用

log.Logger = log.With().Caller().Logger()
log.Info().Msg("hello world")

// Output: {"level": "info", "message": "hello world", "caller": "/go/src/your_project/some_file:21"}

3.uber/zap

go get -u go.uber.org/zap

文档

https://pkg.go.dev/go.uber.org/zap

zap提供了2种logger,分别是Logger和SugaredLogger

在性能要求高但是不是很重要的场景下,适合使用SugaredLogger

logger, _ := zap.NewProduction()
defer logger.Sync() // flushes buffer, if any
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
  // Structured context as loosely typed key-value pairs.
  "url", url,
  "attempt", 3,
  "backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)

输出

{"level":"info","ts":1703922949.209576,"caller":"server/main.go:109","msg":"failed to fetch URL","url":"http://example.com","attempt":3,"backoff":1}
{"level":"info","ts":1703922949.209731,"caller":"server/main.go:115","msg":"Failed to fetch URL: http://example.com"}

在性能要求高且需要类型安全的场景下,适合使用Logger

logger, _ := zap.NewProduction()
defer logger.Sync()
url := "http://example.com"
logger.Info("failed to fetch URL",
	// Structured context as strongly typed Field values.
	zap.String("url", url),
	zap.Int("attempt", 3),
	zap.Duration("backoff", time.Second),
)

输出

{"level":"info","ts":1703923022.603034,"caller":"server/main.go:108","msg":"failed to fetch URL","url":"http://example.com","attempt":3,"backoff":1}

NewExample/NewDevelopment/NewProduction区别

NewExample适用于测试代码,它将DebugLevel及以上级别的日志以JSON格式标准输出,但省略了时间戳和调用函数,以保持示例输出简短和确定。

NewDevelopment适用于开发环境,它以人类友好的格式将DebugLevel及以上级别的日志写入标准错误。

NewProduction适用于生产环境,它将info level及以上级别的日志以JSON格式写入标准错误。

其他参考文档:Go 每日一库之 zap

golang常用库包:log日志记录-uber的Go日志库zap使用详解

4.kratos logrus

参考:go各框架的log日志

5.日志文件回滚

日志文件按时间回滚:natefinch/lumberjack

go get gopkg.in/natefinch/lumberjack.v2

4.strconv包

int转string

s := strconv.Itoa(i)

int64转string

s := strconv.FormatInt(i, 10)

string转int

i, err := strconv.Atoi(s)

string转int64

i, err := strconv.ParseInt(s, 10, 64)

float转string

v := 3.1415926535
s1 := strconv.FormatFloat(v, 'E', -1, 32)//float32
s2 := strconv.FormatFloat(v, 'E', -1, 64)//float64

string转float

s := "3.1415926535"
v1, err := strconv.ParseFloat(v, 32)
v2, err := strconv.ParseFloat(v, 64)

参考:Go语言从入门到精通 - 【精华篇】strconv包详解

5.属性复制

可以使用 jinzhu/copier

使用jinzhu/copier

go get github.com/jinzhu/copier

copy

copier.Copy(&employee, &user)

deepcopy,区别是deepcopy的时候,对dst的属性进行修改,是肯定不会影响src的

var dst User
copier.CopyWithOption(&dst, src, copier.Option{DeepCopy: true})

其他类似的库还有ulule/deepcoper,参考:golang struct拷贝工具(类似于java中 BeanUtils.copyProperties())

以及

https://github.com/jinzhu/copier
https://github.com/mohae/deepcopy
https://github.com/ulule/deepcopier
https://github.com/mitchellh/copystructure
https://github.com/globusdigital/deep-copy
https://github.com/getlantern/deepcopy
https://github.com/antlabs/deepcopy 
https://github.com/go-toolsmith/astcopy
https://github.com/qdm12/reprint
https://github.com/huandu/go-clone
https://github.com/wzshiming/deepclone 
https://github.com/davidwalter0/go-clone

6.切片处理

可以使用samber/lo来对切片、数组或集合进行处理

go get github.com/samber/lo@v1

比如filter操作

even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
    return x%2 == 0
})
// []int{2, 4}

map操作

import "github.com/samber/lo"

lo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {
    return strconv.FormatInt(x, 10)
})
// []string{"1", "2", "3", "4"}

去重操作

uniqValues := lo.Uniq([]int{1, 2, 2, 1})
// []int{1, 2}

获得map的key,并转换成数组

不使用lo

m := map[string]int{
	"foo": 1,
	"bar": 2,
}
keys := make([]string, 0, len(m))
for key := range m {
	keys = append(keys, key)
}

使用lo

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

// [foo bar]

range操作

result := lo.Range(4)
// [0, 1, 2, 3]

result := lo.Range(-4)
// [0, -1, -2, -3]

result := lo.RangeFrom(1, 5)
// [1, 2, 3, 4, 5]

result := lo.RangeFrom[float64](1.0, 5)
// [1.0, 2.0, 3.0, 4.0, 5.0]

result := lo.RangeWithSteps(0, 20, 5)
// [0, 5, 10, 15]

result := lo.RangeWithSteps[float32](-1.0, -4.0, -1.0)
// [-1.0, -2.0, -3.0]

result := lo.RangeWithSteps(1, 4, -1)
// []

result := lo.Range(0)
// []

7.时间处理

可以使用carbon

go get -u github.com/golang-module/carbon/v2

文档

https://pkg.go.dev/github.com/golang-module/carbon/v2

8.断言

可以使用stretchr/testify

go get -u github.com/stretchr/testify

使用assert

import (
  "testing"
  "github.com/stretchr/testify/assert"
)

func TestSomething(t *testing.T) {

  // assert equality
  assert.Equal(t, 123, 123, "they should be equal")

  // assert inequality
  assert.NotEqual(t, 123, 456, "they should not be equal")

  // assert for nil (good for errors)
  assert.Nil(t, object)

  // assert for not nil (good when you expect something)
  if assert.NotNil(t, object) {

    // now we know that object isn't nil, we are safe to make
    // further assertions without causing any errors
    assert.Equal(t, "Something", object.Value)

  }

}

9.resty

在go中请求接口可以使用go-resty/resty框架

go get github.com/go-resty/resty/v2

get请求

// Create a Resty Client
client := resty.New()

resp, err := client.R().
      SetQueryParams(map[string]string{
          "page_no": "1",
          "limit": "20",
          "sort":"name",
          "order": "asc",
          "random":strconv.FormatInt(time.Now().Unix(), 10),
      }).
      SetHeader("Accept", "application/json").
      SetResult(&Result{}).
      SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
      Get("/search_result")

post请求

// Create a Resty Client
client := resty.New()

// POST JSON string
// No need to set content type, if you have client level setting
resp, err := client.R().
      SetHeader("Content-Type", "application/json").
      SetBody(`{"username":"testuser", "password":"testpass"}`).
      SetResult(&AuthSuccess{}).    // or SetResult(AuthSuccess{}).
      Post("https://myapp.com/login")

10.单元测试

使用testing框架进行单元测试

package dao

import (
	"fmt"
	"gin-template/internal/database"
	"gin-template/internal/model"
	"testing"
)

func Test_userDo_Create(t *testing.T) {
	user := model.User{
		Username: "test",
		Email:    "test@test",
	}
	SetDefault(database.DB)

	err := User.Create(&user)
	if err != nil {
		fmt.Println("creat user")
	}
}

如果遇到go testing flag provided but not defined: -test.v的报错,解决方法是添加一个init.go文件

package test

import "testing"

func init() {
	testing.Init()
}

然后在使用了flag的地方添加的import中

_ "gin-template/internal/test"

参考:问题记录:flag provided but not defined: -test.v 异常处理过程

11.打桩工具——go monkey

go monkey可以用于在单元测试中进行打桩(指补齐未实现的代码)

参考:Go单测从零到溜系列4—使用monkey打桩

 

posted @ 2016-03-18 09:42  tonglin0325  阅读(296)  评论(0编辑  收藏  举报