• golang语法笔记


    开始微服务,那就先温习下golang语法吧;

     

    golang变量类型

    1. 整形

     

    Go

    %b    表示为二进制

    %c    该值对应的unicode码值

    %d    表示为十进制

    %o    表示为八进制

    %q    该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示

    %x    表示为十六进制,使用a-f

    %X    表示为十六进制,使用A-F

    %U    表示为Unicode格式:U+1234,等价于"U+%04X"

    %E    用科学计数法表示

    %f    用浮点数表示

    2. 浮点型

    • • float32的精度只能提供大约6个十进制数(表示后科学计数法后,小数点后6位)的精度
    • • float64的精度能提供大约15个十进制数(表示后科学计数法后,小数点后15位)的精度

    3. Byte rune

     

    Go

    var a byte = 65   byte,占用1个节字

    var b rune = 'B'   rune,占用4个字节

    4. String

    转义:

     

    Go

    var mystr02 string = ` `

    var mystr01 string = "\r\n"

    换行:

    var mystr01 string = `你好呀!

    我的公众号是: Go欢迎大家关注`

    5. Array

     

    Go

    // 第一种方法

    var arr [3]int = [3]int{1,2,3}

    // 第二种方法

    arr := [3]int{1,2,3}

    arr := [...]int{1,2,3}

    6. 切片

    区间: 左闭右开

     

    Apache

    arr[0:2]  --> 1,2

    sliTest := make([]int, 2, 3)

    sliTest[1] = 5

    sliTest[0] = 1

    sliTest = append(sliTest, 6)

    7. Map

     

    Go

    mapA := map[int]string{1:"one",2:"two"}

    mapA[6] = "six"

    //进行判断key是否存在:

    if math, ok := mapA[6]; ok {

    }

    //循环遍历:key+value

    for key,value := range mapA{

       fmt.Println(key,value)

    }

    //循环遍历:key

    for key := range mapA{

       fmt.Println(key)

    }

    //循环遍历:value

    for _, value := range mapA{

       fmt.Println(value)

    }

    8. Ptr

    指针

     

    Go

    ptr := new(string)

    *ptr = "string ptr"

    str := *ptr

    ptr2 := &str

    in1t := 1

    ptr3 := &in1t

    ptr4 := &(*ptr3)

    println("name:",sliTest)

    9. Switch case

     

    Go

    func getResult(args ...int) bool {

        return true

    }

    //接函数

    switch getResult(chinese, english, math) {

        // case 后也必须是布尔类型

        case true:

            fmt.Println("该同学所有成绩都合格")

        case false:

            fmt.Println("该同学有挂科记录")

    }

    //这种 可以替代if else

    score := 30

    switch {

        case score >= 95 && score <= 100:

            fmt.Println("优秀")

        case score >= 80:

            fmt.Println("良好")

        case score >= 60:

            fmt.Println("合格")

        case score >= 0:

            fmt.Println("不合格")

        default:

            fmt.Println("输入有误...")

    }

    //穿透性

    s := "hello"

    switch {

        case s == "hello":

            fmt.Println("hello")

            fallthrough //直接不比较就穿透到下面的case,不进行break

        case s != "world":

            fmt.Println("world")

        }

    output: hello world

    10. For

    for [condition |  ( init; condition; increment ) | Range]{

       statement(s);

    }

    • • condition: 接一个条件表达式

     

    Go

    a := 1

    for a <= 5 {

        fmt.Println(a)

        a ++

    }

    • • ( init; condition; increment ): 接三个表达式

     

    Go

    for i := 1; i <= 5; i++ {

        fmt.Println(i)

    }

    • • Range: 接一个 range 表达式

     

    Go

    for key, value := range myarr {

        fmt.Println(key,value)

    }

    • • 不接表达式

     

    Go

    for {

        fmt.Println("无限")

    }

    // 等价于

    for ;; {

        fmt.Println("无限")

    }

    11. Goto

     

    Go

    if name == "lern" {

          goto flagEnd

       }

       goto flag

       //var i = 0  这个会报错,flag间不允许定义

    flag:

       fmt.Println("flag")

       fmt.Println("flag2")

       fmt.Println("flag3")

    flagEnd:

       fmt.Println("flagEnd")

    12. Defer

     

    Go

    func myfunc() {

        fmt.Println("B")

    }

    func main() {

        defer myfunc()

        fmt.Println("A")

    }

    输出:

    A

    B

    //先后顺序:  遵循栈的方式

    func main() {

        name := "go"

        defer fmt.Println(name) // 输出: go

        name = "python"

        defer fmt.Println(name) // 输出: python

        name = "java"

        fmt.Println(name)

    }

    输出:

    java

    python

    go

    13. interface{}

     

    Go

    print("string")

    func print(param interface{}){

        if value, ok := param.(string); ok{

            fmt.Println(value)      

        }

    }

    14. Select case

    • • select 只能用于 channel 的操作(写入/读出),而 switch 则更通用一些;
    • • select 的 case 是随机的,而 switch 里的 case 是顺序执行;
    • • select 要注意避免出现死锁,同时也可以自行实现超时机制;
    • • select 里没有类似 switch 里的 fallthrough 的用法;
    • • select 不能像 switch 一样接函数或其他表达式。

     

    Go

    func main() {

        c1 := make(chan string, 1)

        c2 := make(chan string, 1)

        c2 <- "hello"

        select {

        case msg1 := <-c1:

          fmt.Println("c1 received: ", msg1)

        case msg2 := <-c2:

          fmt.Println("c2 received: ", msg2)

        default:

          fmt.Println("No data received.")

        }

    }

    15. panic

    异常,可以采用recover来进行恢复

     

    Go

    func set_data(x int) {

        defer func() {

            // recover() 可以将捕获到的panic信息打印

            if err := recover(); err != nil {

                fmt.Println(err)

            }

        }()

        // 故意制造数组越界,触发 panic

        var arr [10]int

        arr[x] = 88

    }

    func main() {

        set_data(20)

        // 如果能执行到这句,说明panic被捕获了

        // 后续的程序能继续运行

        fmt.Println("everything is ok")

    }

    输出:

    runtime error: index out of range [20] with length 10

    everything is ok

    golang面向对象

    1. Struct

     

    Go

    //基类

    type base struct {

       name string

    }

    type userinfo struct {

       name string

       age int

       base  //组合

    }

    func(self *userinfo) dump() {

       fmt.Println(self.name) //输出的是qingfeng

       fmt.Println(self.age)

    }

    func main()  {

       bb := base{name: "qqq"}

       user := userinfo{

          name: "qingfeng",

          age:  19,

          base: bb,

       }

       user.dump()

       fmt.Println(user.base.name) //这个才输出 qqq

    }

    2. Interface

     

    Go

    type caller interface {

       test1()

       test2()

    }

    type son struct {

       userid int

       username string

    }

    type mun struct {

       userid int

       username string

    }

    func (self son) test1()  {

       fmt.Println("son_test1",self.userid)

    }

    func (self son) test2()  {

       fmt.Println("son_test2",self.username)

    }

    func (self mun) test1()  {

       fmt.Println("mun_test1",self.userid)

    }

    func (self mun) test2()  {

       fmt.Println("mun_test2",self.username)

    }

    m_son := son{

       userid:   1001,

       username: "1001",

    }

    m_mun := mun{

       userid:   1005,

       username: "1005",

    }

    m_calls := []caller{m_son, m_mun}

    for _,v := range m_calls{

       v.test1()

       v.test2()

    }

    3. x.()

     

    Go

    func findType(i interface{}) {

        switch x := i.(type) {

        case int:

            fmt.Println(x, "is int")

        case string:

            fmt.Println(x, "is string")

        case nil:

            fmt.Println(x, "is nil")

        default:

            fmt.Println(x, "not type matched")

        }

    }


    var k interface{} // nil

    t3, ok := k.(interface{})

    fmt.Println(t3, "-", ok)

    var k int // nil

    t3, ok := k.(int)

    fmt.Println(t3, "-", ok)

    4. reflect

     

    Go

    func main() {

        var name string = "Go编程时光"

        fmt.Println("真实世界里 name 的原始值为:", name)

        v1 := reflect.ValueOf(&name)

        v2 := v1.Elem()

        v2.SetString("Python编程时光")

        fmt.Println("通过反射对象进行更新后,真实世界里 name 变为:", name)

    }

    内容太多:参考文章,写的挺详细的

    http://golang.iswbm.com/en/latest/c02/c02_08.html

    5. Func

     

    Go

    func double(a int) (b int) {

        // 不能使用 := ,因为在返回值哪里已经声明了为int

         b = a * 2

        // 不需要指明写回哪个变量,在返回值类型那里已经指定了

         return

     }

     

     func main() {

         fmt.Println(double(2))

    }

    output: 4

    6. chan

     

    Go

    func main() {

        pipline := make(chan int, 10)

        fmt.Printf("信道可缓冲 %d 个数据 ", cap(pipline))

        pipline<- 1

        fmt.Printf("信道中当前有 %d 个数据", len(pipline))

    }

    定义只读信道

     

    Go

    var pipline = make(chan int)

    type Receiver = <-chan int // 关键代码:定义别名类型

    var receiver Receiver = pipline

    定义只写信道

     

    Go

    var pipline = make(chan int)

    type Sender = chan<- int  // 关键代码:定义别名类型

    var sender Sender = pipline

    仔细观察,区别在于 <- 符号在关键字 chan 的左边还是右边。

    • • <-chan 表示这个信道,只能从里发出数据,对于程序来说就是只读
    • • chan<- 表示这个信道,只能从外面接收数据,对于程序来说就是只写

    chan用来做锁

     

    Go

    // 由于 x=x+1 不是原子操作// 所以应避免多个协程对x进行操作// 使用容量为1的信道可以达到锁的效果

    func increment(ch chan bool, x *int) {

        ch <- true

        *x = *x + 1

        <- ch

        }

    func main() {

        // 注意要设置容量为 1 的缓冲信道

        pipline := make(chan bool, 1)

        var x int

        for i:=0;i<1000;i++{

            go increment(pipline, &x)

        }

        // 确保所有的协程都已完成

        // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep

        time.Sleep(3)

        fmt.Println("x 的值:", x)

        }

     

    7. WaitGroup

    等待协程结束

    • • Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量
    • • Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 defer 来调用。
    • • Wait:阻塞当前协程,直到实例里的计数器归零。

     

    Go

    func worker(x int, wg *sync.WaitGroup) {

        defer wg.Done()

        for i := 0; i < 5; i++ {

            fmt.Printf("worker %d: %d ", x, i)

        }

    }

    func main() {

        var wg sync.WaitGroup

        wg.Add(2)

        go worker(1, &wg)

        go worker(2, &wg)

        wg.Wait()

    }

    8. Mutex

    互斥锁:



    Go

    func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex) {

        for i := 0; i < 1000; i++ {

            lock.Lock() //加锁

            *count = *count + 1

            lock.Unlock() //解锁

        }

        wg.Done()

    }

    func main() {

        var wg sync.WaitGroup

        lock := &sync.Mutex{}

        count := 0

        wg.Add(3)

        go add(&count, &wg, lock)

        go add(&count, &wg, lock)

        go add(&count, &wg, lock)

        wg.Wait()

        fmt.Println("count 的值为:", count)

    }

    读写锁:

     

    Go

    func main() {

        lock := &sync.RWMutex{}

        lock.Lock()

        for i := 0; i < 4; i++ {

            go func(i int) {

                fmt.Printf("第 %d 个协程准备开始... ", i)

                lock.RLock()

                fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁 ", i)

                time.Sleep(time.Second)

                lock.RUnlock()

            }(i)

        }

        time.Sleep(time.Second * 2)

        fmt.Println("准备释放写锁,读锁不再阻塞")

        // 写锁一释放,读锁就自由了

        lock.Unlock()

        // 由于会等到读锁全部释放,才能获得写锁

        // 因为这里一定会在上面 4 个协程全部完成才能往下走

        lock.Lock()

        fmt.Println("程序退出...")

        lock.Unlock()

    }

    9. goroutine

    go的协程,理解成语言级别的

     

    Go

    func mygo(name string) {

        for i := 0; i < 10; i++ {

            fmt.Printf("In goroutine %s ", name)

            // 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠

            time.Sleep(10 * time.Millisecond)

        }

    }

    func main() {

        go mygo("协程1号") // 第一个协程

        go mygo("协程2号") // 第二个协程

        time.Sleep(time.Second)

    }

    golang环境:

    1. Go mod

    • • go mod init:初始化go mod, 生成go.mod文件,后可接参数指定 module 名,上面已经演示过。
    • • go mod download:手动触发下载依赖包到本地cache(默认为$GOPATH/pkg/mod目录)
    • • go mod graph: 打印项目的模块依赖结构
    • • go mod tidy :添加缺少的包,且删除无用的包
    • • go mod verify :校验模块是否被篡改过
    • • go mod why: 查看为什么需要依赖
    • • go mod vendor :导出项目所有依赖到vendor下
    • • go mod edit :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 -require=golang.org/x/text 添加依赖,接 -droprequire=golang.org/x/text 删除依赖,详情可参考 go help mod edit
    • • go list -m -json all:以 json 的方式打印依赖详情

    golang编码规范:

    1. 文件命名

    • • 由于 Windows平台文件名不区分大小写,所以文件名应一律使用小写
    • • 不同单词之间用下划线分词,不要使用驼峰式命名
    • • 如果是测试文件,可以以 _test.go 结尾
    • • 文件若具有平台特性,应以 文件名_平台.go 命名,比如 utils_ windows.go,utils_linux.go,可用的平台有:windows, unix, posix, plan9, darwin, bsd, linux, freebsd, nacl, netbsd, openbsd, solaris, dragonfly, bsd, notbsd, android,stubs
    • • 一般情况下应用的主入口应为 main.go,或者以应用的全小写形式命名。比如MyBlog 的入口可以为 myblog.go

    2. 常量命名

    目前在网络上可以看到主要有两种风格的写法

    • • 第一种是驼峰命名法,比如 appVersion
    • • 第二种使用全大写且用下划线分词,比如 APP_VERSION

    这两种风格,没有孰好孰弱,可自由选取,我个人更倾向于使用第二种,主要是能一眼与变量区分开来。

    如果要定义多个变量,请使用 括号 来组织。

     

    Go

    const (

        APP_VERSION = "0.1.0"

      CONF_PATH = "/etc/xx.conf")

    3. 变量命名

    和常量不同,变量的命名,开发者们的喜好就比较一致了,统一使用 驼峰命名法

    • • 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u
    • • 若该变量为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头。例如:isExist ,hasConflict 。
    • • 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。
    • • 若变量中有特有名词(以下列出),且变量为私有,则首单词还是使用全小写,如 apiClient。
    • • 若变量中有特有名词(以下列出),但变量不是私有,那首单词就要变成全大写。例如:APIClient,URLString

    这里列举了一些常见的特有名词:

     

    Go

    // A GonicMapper that contains a list of common initialisms taken from golang/lintvar LintGonicMapper = GonicMapper{

        "API":   true,

        "ASCII": true,

        "CPU":   true,

        "CSS":   true,

        "DNS":   true,

        "EOF":   true,

        "GUID":  true,

        "HTML":  true,

        "HTTP":  true,

        "HTTPS": true,

        "ID":    true,

        "IP":    true,

        "JSON":  true,

        "LHS":   true,

        "QPS":   true,

        "RAM":   true,

        "RHS":   true,

        "RPC":   true,

        "SLA":   true,

        "SMTP":  true,

        "SSH":   true,

        "TLS":   true,

        "TTL":   true,

        "UI":    true,

        "UID":   true,

        "UUID":  true,

        "URI":   true,

        "URL":   true,

        "UTF8":  true,

        "VM":    true,

        "XML":   true,

        "XSRF":  true,

        "XSS":   true,}

    4. 函数命名

    • • 函数名还是使用 驼峰命名法
    • • 但是有一点需要注意,在 Golang 中是用大小写来控制函数的可见性,因此当你需要在包外访问,请使用 大写字母开头
    • • 当你不需要在包外访问,请使用小写字母开头
    • • 另外,函数内部的参数的排列顺序也有几点原则
    • • 参数的重要程度越高,应排在越前面
    • • 简单的类型应优先复杂类型
    • • 尽可能将同种类型的参数放在相邻位置,则只需写一次类型

    5. 接口命名

    使用驼峰命名法,可以用 type alias 来定义大写开头的 type 给包外访问。

     

    Go

    type helloWorld interface {

        func Hello();

        }

    type SayHello helloWorld

    当你的接口只有一个函数时,接口名通常会以 er 为后缀

    type Reader interface {

        Read(p []byte) (n int, err error)

        }

    6. 注释规范

    注释分为

    6.1 代码注释

    用于解释代码逻辑,可以有两种写法

    单行注释使用 // ,多行注释使用 /* comment */

     

    Go

    // 单行注释

    /*多行注释*/

    另外,对于代码注释还有一些更加苛刻的要求,这个看个人了,摘自网络:

    • • 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。
    • • 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。
    • • 包、函数、方法和类型的注释说明都是一个完整的句子。
    • • 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。
    • • 注释的单行长度不能超过 80 个字符。
    • • 类型的定义一般都以单数形式描述:

     

    Go

    // Request represents a request to run a command.  type Request struct { ...

    • • 如果为接口,则一般以以下形式描述:

     

    Go

    // FileInfo is the interface that describes a file and is returned by Stat and Lstat.type FileInfo interface { ...

    • • 函数与方法的注释需以函数或方法的名称作为开头:

     

    Go

    // Post returns *BeegoHttpRequest with POST method.

    • • 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述:

     

    Go

    // Copy copies file from source to target path.// It returns false and error when error occurs in underlying function calls.

    • • 若函数或方法为判断类型(返回值主要为 bool 类型),则以 <name> returns true if 开头:

     

    Go

    // HasPrefix returns true if name has any string in given slice as prefix.func HasPrefix(name string, prefixes []string) bool { ...

    6.2 特别注释

    • • TODO:提醒维护人员此部分代码待完成
    • • FIXME:提醒维护人员此处有BUG待修复
    • • NOTE:维护人员要关注的一些问题说明

    7. 包的导入

    单行的包导入

     

    Go

    import "fmt"

    多个包导入,请使用 {} 来组织

    import {

      "fmt"

      "os"}

    另外根据包的来源,对排版还有一定的要求

    • • 标准库排最前面,第三方包次之、项目内的其它包和当前包的子包排最后,每种分类以一空行分隔。
    • • 尽量不要使用相对路径来导入包。

     

    Go

    import (

        "fmt"

        "html/template"

        "net/http"

        "os"

        "github.com/codegangsta/cli"

        "gopkg.in/macaron.v1"

        "github.com/gogits/git"

        "github.com/gogits/gfm"

        "github.com/gogits/gogs/routers"

        "github.com/gogits/gogs/routers/repo"

        "github.com/gogits/gogs/routers/user"

    )

    8. gofmt

    有空的可以用gofmt来进行格式化;

    参考:https://jingyan.baidu.com/article/c45ad29c64cfe7051653e245.html

     

  • 相关阅读:
    bioinformatics使用原始fq.gz文件生成一个更小的fastq文件
    nf学习笔记
    syntax error near unexpected token `done’
    perl——持续更新
    千万别用PATH=$PATH:/配置Linux环境变量!
    nextflow学习笔记——持续更新
    json学习笔记
    shell(Linux)怎样用awk查找满足条件的行列并且计算平均值
    模板事务的几种写法和总结
    适配器的应用(泛型模式)
  • 原文地址:https://www.cnblogs.com/left69/p/14241760.html
Copyright © 2020-2023  润新知