go语言基础

#go是静态类型语言,一旦变量被声明,它的类型就不可能被改变
#变量存在有效区域,例如在花括号内的范围,子括号继承父括号
var bianliang = 10 #普通声明变量
bianliang := 10 #短声明,和普通声明一样,只可以在函数内使用,但是可以在无法使用var方法的情况中使用
#需要注意的是,如果要和浮点数进行计算,那么一开始就要指定为浮点数:
var bianliang float64 = 10

varint-- #等于varint = varint -1
varint++ #等于varint = varint +1
|| #或
&& #和
!var #变量前加感叹号相反,例如!= 就是不等于

if> else if>else #判断,在go中else if 必须和上一行的结尾在同一行不可单独一行

    if bianliang == "你好" { //判断
        fmt.Print("变量是你好") //打印
    } else if bianliang == "不好" { //否则再判断
        fmt.Print("变量是不好")
    } else if bianliang == "不好啦" { //否则再判断
        fmt.Print("变量是不好啦")
    }else { //否则
        fmt.Print("hello world!")
    }

switch>case>default #类似if 对同一个变量不停的比较,它还有一个fallthrough参数,放到某个case 中,如果匹配了这个case则会把下一个case也执行

var bian = "你好"
switch bian {
case "你好啦":
    fmt.Print("变量是:你好啦")
case "你好":
    fmt.Print("变量是:你好")
    fallthrough  //将会把下面的case也执行
case "你好么":
    fmt.Print("变量是:你好么")
default:
    fmt.Print("未匹配到结果后默认执行")
}

for #循环,如果没有条件则无限循环,附带条件则根据条件循环,使用break跳出循环

    var bian = 10
    for bian > 0 {  //限制条件内循环
        fmt.Println(bian)
        bian--
    }


for i := int64(0); i < 5; i++ {
    //循环5次
}

nil #代表0或空,同python的None一样

fmt.Println() #打印,换行
fmt.Print() #打印,不会换行
log.Printf("购物车数据: %+v\n", ordermx_cha) #格式化打印数组和对象

整数类型

int #32位上默认为int32,64位设备上默认为int64
uint #同上
int8 #-128127 #数字范围
uint8 #0
255
int16 #-3276832767
uint16 #0
65535
int32 #-21474832147483647
uint32 #0
4294967295
int64 #-922337203685477592233720368543775807
uint64 #0
18446744073709551615

超大数值

import (
    "fmt"
    "math/big"
)

func main() {
    bian := big.NewInt(9959)  //添加新的big变量
    bian2 := big.NewInt(9999) //添加新的big变量
    result := new(big.Int) //创建一个新值类型为big
    result.Add(bian, bian2)  //执行相加操作
    fmt.Print(result)  //打印结果
    
    //假设bian2为int6类型,可以使用 new(big.Int).SetInt64(int64(bian2))转换为big类型
}

数值字符串互转

import (
    "fmt"
    "strconv"
)

//数值转字符串
func main() {
    aa := strconv.FormatUint(dsdfd) //uint转字符串
    aab := strconv.FormatUint(dsdfd,10) //uint64转字符串
    ddd := 5555
    eee := zhustrconv.Itoa(ddd) //Int转字符串
    fmt.Sprint(dgform.Shangpin)  //数值转字符串
    aaa := "哈哈"
    fff := fmt.Sprintf("这里是字符串 %v 插入", aaa) //通过占位符插入,其中v代表默认值,f代表浮点数类型,d代表整数类型
    bbb := "我很好"
    ccc := eee + fff + bbb
    fmt.Println(ccc)
}

//字符串转数值
func ceshi() {
    str := "3.14159"                      //浮点数字符串
    f, err := strconv.ParseFloat(str, 64) //字符串转浮点数,并接受错误,第二参数为转换后的位数是64位
    if err != nil {
        fmt.Println("转换失败:", err)
    } else {
        fmt.Printf("浮点数值: %f\n", f)
    }
}

func ceshi2() {
    str1 := "42"
    i, err := strconv.ParseInt(str1, 10, 64) //字符串转int,10进制,64位
    if err != nil {
        fmt.Println("转换失败:", err)
    } else {
        fmt.Printf("整数值: %d\n", i)
    }

    str2 := "255"
    u, err := strconv.ParseUint(str2, 10, 64) //字符串转Uint
    if err != nil {
        fmt.Println("转换失败:", err)
    } else {
        fmt.Printf("无符号整数值: %d\n", u)
    }
}

字符串拼接

fmt.Sprintf 使用占位符拼接不同类型的字符,并返回字符串,常用的占位符有
%s:用于字符串(string)。它将相应的变量作为字符串进行插入。
%d:用于整数(int、int32、int64 等)。它将相应的变量作为十进制数进行插入。
%f:用于浮点数(float32、float64)。你可以指定小数点后的精度,例如 %.2f 用于格式化浮点数并保留两位小数。
%t:用于布尔值(bool)。它将 true 或 false 插入到字符串中。
%v:万用占位符。它会根据相应变量的类型自动选择格式。对于大多数变量,它等同于使用默认格式。
%+v:类似于 %v,但对于结构体,它还会添加字段名。
%#v:用于打印值的 Go 语言语法表示。例如,对于字符串,它会包含引号。
%p:用于指针,输出指针的内存地址。

小数转换

//保留4位小数,并附带转为字符串格式了
    cny2 := fmt.Sprintf("%.4f", cnyf)
    //如果不足4位,去掉尾部多余0
    cny := strings.TrimRight(cny2, "0")

一个完整的简易示范

package main

import (
    "fmt"
)

func main() { //花括号代表程序内,变量也定义到程序内
    // var bianliang string //定义类型
    // bianliang = "你好"     // 赋值
    var bianliang = "你好"  //定义变量并赋值
    if bianliang == "你好" { //判断
        fmt.Print("变量是你好") //打印
    } else if bianliang == "不好" { //否则再判断
        fmt.Print("变量是不好")
    } else { //否则
        fmt.Print("hello world!")
    }
}

指针

//&号指针的值,*号代表指针的地址
var a *int //声明一个变量指针
&a //代表指针的值

可以理解为 *就是留了一个空房子的门牌号,只是里面没住人,可以晚点给它赋值,而&则代表房子里面的人

语言结构体,相当于类

//结构体的作用是用于多组相同结构的数据

package main

import "fmt"

type Books struct { //定义一个结构体
   Title   string //结构体首字母命名一定要大写
   Author  string //定义结构体的类型和变量
   Subject string
   Book_id int
}

func main() {
   var aa Books    // 申明一个变量为Books类型的结构体
   aa.title = "标题" //通过对象访问子类
   aa.author = "作者"
   aa.book_id = 214
   aa.subject = "主题"
   fmt.Println(aa)
}

//通过结构体,访问子类的函数

package main

import "fmt"

type Books struct { //定义一个结构体
}

type Books2 struct {
}

func (t *Books) fangfa() { //定义一个匿名函数,如果将参数类型指定某个结构体,并定义一个方法,则该可以通过对象执行这个子方法
    fmt.Println("这是book1")
}

func (t *Books2) fangfa() {
    fmt.Println("这是book2")
}

func main() {
    var aa Books // 申明一个变量为Books类型的结构体
    aa.fangfa() //通过对象访问子类方法
}

//通过接口来抽象化结构体

package main

import "fmt"

type jiekou interface {
    fangfa()
}

type Books struct { //定义一个结构体
}

type Books2 struct { //定义另一个结构体
}

func (t *Books) fangfa() { //定义一个结构体类型方法
    fmt.Println("这是book1")
}

func (t *Books2) fangfa() { //定义另一个结构体类型方法
    fmt.Println("这是book2")
}

func main() {
    var aa jiekou // 申明一个变量为jiekou的接口类型
    aa = &Books{} //将变量指定为Books类型
    aa.fangfa()   //访问方法
}

使用接口和直接使用结构体有两个不同:
多态性:当一个变量指定为接口类型后,就可以在不同的结构体类型之间变换类型。
扩展性:在以后改动代码的时候,例如增加了新的结构体方法,只需要改更少的代码,直接将变量改成不同的结构体类型赋值即可

定义函数与返回值

package main

import (
    "fmt"
)

func main() {
    a, b, c := ceshi(5, 6, "哈哈")
    fmt.Print(a, b, c)
}

func ceshi(num1, num2 int, z string) (int, int, string) { //参数一二为整数,参数三为字符串,返回自如内容为 整数 整数,字符串
    aaa := num1 + num2
    bbb := num1 - num2
    ccc := z
    return aaa, bbb, ccc
}

快速定义函数实参

package main

import (
   "fmt"
   "math"
)

func main(){
   /* 声明函数变量 */
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }

   /* 使用函数 */
   fmt.Println(getSquareRoot(9))

}

并发

在调用子函数的时候,加上 go 即可

go f(x, y, z) //它会在一个新的协程中执行f函数

获取协程返回值使用通道

package main

import (
    "fmt"
    "time"
)

// 假设这是一个发送邮件的函数,并返回一个表示成功或失败的布尔值
func sendEmail() bool {
    // 模拟邮件发送的耗时操作
    time.Sleep(2 * time.Second)
    return true // 假设邮件发送成功
}

func main() {
    // 创建一个传递布尔值的通道
    ch := make(chan bool)

    // 启动一个协程来发送邮件
    go func() {
        // 调用 sendEmail 并将结果发送到通道
        ch <- sendEmail()
    }()

    // 从通道接收返回值
    result := <-ch

    // 打印结果
    fmt.Println("邮件发送状态:", result)

    // 关闭通道(可选,但是在大型应用中是一个好习惯)
    close(ch)
}

接口类型,看不懂还需要研究

package main

import (
    "fmt"
)

// 定义一个接口类型 Phone,该接口要求实现一个 call 方法
type Phone interface {
    call()
}

// 定义一个结构体类型 NokiaPhone,表示诺基亚手机
type NokiaPhone struct {
}

// 实现 Phone 接口的 call 方法,用于输出诺基亚手机的呼叫信息
func (nokiaPhone NokiaPhone) call() {
    fmt.Println("我是诺基亚手机,我可以给你打电话!")
}

// 定义一个结构体类型 IPhone,表示iPhone手机
type IPhone struct {
}

// 实现 Phone 接口的 call 方法,用于输出iPhone手机的呼叫信息
func (iPhone IPhone) call() {
    fmt.Println("我是iPhone手机,我可以给你打电话!")
}

func main() {
    // 声明一个变量 phone,它是一个 Phone 接口类型的变量
    var phone Phone

    // 创建一个 NokiaPhone 类型的实例,并将其赋值给 phone 变量
    phone = new(NokiaPhone)

    // 调用 phone 变量的 call 方法,这里实际上调用了 NokiaPhone 结构体的 call 方法
    phone.call()

    // 将 phone 变量赋值为一个 IPhone 类型的实例
    phone = new(IPhone)

    // 再次调用 phone 变量的 call 方法,这次实际上调用了 IPhone 结构体的 call 方法
    phone.call()
}

包管理

  • go get -u github.com/gin-gonic/gin //下载包
  • go list -m all //查看所有包
  • 查看远程包下载的包路径:
    $GOPATH/pkg/mod/ //在go环境目录下

自己写包调用

包的注意事项:
一个文件夹下只能有一个包名,可以是多个go文件
导入包就直接导入文件夹,包不能互导,如果两个包都要用到的写到第三个包

假设项目根目录下有子目录routers,然后创建了一个router.go的文件:

package router  //调用的时候会已这个包名为准,和文件名无关

import (
    "github.com/gin-gonic/gin" //导入gin包
    "net/http"
)

func Router() *gin.Engine {  //创建了一个路由函数,并指定类型为gin引擎
    r := gin.Default() //初始化gin

    r.GET("/", func(ctx *gin.Context) { //定义了一个路由和方法,定义了一个*gin.Context的上下文参数,类似django视图的request参数
        ctx.String(http.StatusOK, "你好,gin") //// 返回一个HTTP响应成功状态,状态码为200
    })
    
    user := r.Group("/user")  //创建分组路由
    {
        user.POST("/post", func(ctx *gin.Context) { //继承分组路由,此时这里应是/user/post
            ctx.String(http.StatusOK, "这是post")
        })
    
        user.GET("/", func(ctx *gin.Context) {
            ctx.String(http.StatusOK, "这是get分组")
        })
    
        user.DELETE("/del", func(ctx *gin.Context) {
            ctx.String(http.StatusOK, "这是del")
        })
    }
    return r //返回r
}

假设根目录下有main.go:

package main

import (
    "GinDemo/routers" //导入routers目录下的包
)

func main() {
    r := router.Router() //引用包名router下的Router方法
    r.Run(":80") // 启动HTTP服务器,监听端口80
}

遍历数组

//i是遍历的索引号,name是内容
for i, name := range names {
    fmt.Print(name)
}

创建空数组:msg_About := make([]string, 11)
数组追加:Urls = append(Urls, lists1) //后面参数是追加内容

map映射

//初始化一个map变量,键值类型为uint,值类型为uint16
mapvar := make(map[uint]uint16) 
//遍历某个数组
for _, cart := range tjcart.Carts {
    为mapvar的键值赋值
    mapvar[cart.Cartid] = cart.Number
    }

常见错误排查

no new variables on left side of :=
左侧没有新变量
检查是否应该使用 = 而不是:=,例如变量err在之前已经被定义,则因该使用=,但是往往err伴随其他返回值,所以要么先定义好其他返回值的变量,然后使用=,或者直接将err重新命名使用 :=

too many return values
    have (error)
    want ()

返回值太多
应该是函数顶部没有定义返回的类型,而返回了值,或者返回类型和数量和定义的不匹配