Golang 笔记

2019-01-24  语言  ,  756

官网

官网非常好的教程https://tour.go-zh.org

Go的50度灰:Golang新开发者要注意的陷阱和常见错误 https://colobu.com/2015/09/07/gotchas-and-common-mistakes-in-go-golang/#

别人写的非常好的笔记,强推astaxie/build-web-application-with-golang

别人写的笔记https://www.zybuluo.com/octopus/note/1212993

安装相关

直接

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.io,direct

以下可以不用看了

必须要翻墙!!!!

挂github的代理,如果要用socks5的话,必须做个二级代理转成http的,可以使用cow

然后

git config --global http.proxy "http://127.0.0.1:7777"
export http_proxy="http://127.0.0.1:7777"
export https_proxy="http://127.0.0.1:7777"
export all_proxy="http://127.0.0.1:7777"

win 下的 cmd 需要把 export 换成 set,powershell 需要换成 $Env:

IDE用vscode,装上go的插件。

然后随便新建一个xxx.go,然后会提示你装go的一些包,此时直接按install all

若翻墙失效,在 vs 的终端里会告诉你哪些包失效。

复制这些失效的包地址(包括github和golang.org的包),然后手动用git bash来装,具体来说就是

go get -u -v xxxxx

xxxxx是上面得到的包地址。

其他

go自带命令介绍可以看

go buildgo test执行本目录下所有目录(递归),参考golang-tests-in-sub-directory,即我们用./...即可

笔记

这个语言有着一个特别奇葩的语言规范。。。。

  • 包内函数名第一个字母是大写则为可见(public),否则为隐藏(private)。这一点特别奇葩
  • 变量类型是放在变量名后面。。。。函数返回值也是放在函数名后面。。。
  • _(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。
  • 函数可以返回多个值demo
  • 返回值可以命名,命名的返回值相当于在函数顶部定义了该变量demo
  • 在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明。
  • 函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用。
  • 基本类型
  • 错误类型是error,新建一个字符串提示的错误可以用errors.New("blabla")
  • 字符串是不能更改的,不过可以这样
s := "hello"
c := []byte(s)  // 将字符串 s 转换为 []byte 类型
c[0] = 'c'
s2 := string(c)  // 再转换回 string 类型
  • iota关键字可以用来替代enumconst里面每行就增1,其每遇到一个const就会重置为0
const (
    a       = iota //a=0
    b       = "B"
    c       = iota             //c=2
    d, e, f = iota, iota, iota //d=3,e=3,f=3
    g       = iota             //g = 4
)
  • 无隐式转换
  • 数值常量可以很大,其一开始是用高精度存储的,比如const Big = 1 << 100是合法的,用Big >> 80相当于int(1 << 20)
  • 结构体匿名字段,会像继承一样将元素组合进来,demo
  • 获取类型:a.(type),但只能用于switch的语句里
  • 类型断言:value, ok := a.(int),若ok == true表示a的类型的确是int,且值为valuedemo
  • 函数参数的开销(拷贝与指针)比较:文章。总之就是,大一点的结构体/基本数据结构就用指针吧。。。
  • 没有while,用没有分号的for来代替了关键字while
  • forifswitch可以不写圆括号,但大括号必须写
  • if可以在条件语句前加前置语句,在前置语句定义的变量作用域在if内,且能被对应的else使用demo
  • switch同样可以加前置语句,不用加breakcase的值可以不是常量!switch也能没有条件语句!这样可以代替多个if-then-else语句!demo
  • 比较奇葩的关键字defer,该语句调用的函数会在外函数执行完后才执行(但参数是立即计算)demo,这个关键字的好处在于可以避免一些bug的发生demo
  • 多条defer会以栈的方式(后进先出)来执行
  • 没有指针运算
  • 数组切片只是引用,不是复制了一份数据
  • 数组有lencap的属性,最好看例子来理解demo1demo2
  • 切片详细请看文章
  • 若想给某个类型加方法(Type.Func),Type要么在同一包内定义,要么得type myType Type,然后再写新方法myType.Funcdemo
  • go里的指针似乎就是结合了c里的指针和引用?demo1demo2
  • 指针接受者能改变数据,值接受者不行。具体比较看demo
  • 接口可以用c++的基类指针来理解
  • 接口实现若为“值接受者”,“指针/值"都能使用;但若为“指针接受者”,则只有“指针才能使用”。demo
  • 接口要注意一下nildemo
  • 给一个好玩的抽象类玩法
package main

import "fmt"

type A struct {
    X string
}

func (a *A) G() *A {
    return a;
}

func (a *A) fu() string {
    return a.X
}

type B struct {
    * A
}

type C interface {
    G() *A
    fu() string
}

func main() {
    x := A{X: "hehe"}
    y := B{A: &A{X: "hehe"}}
    fmt.Println(x.fu())
    fmt.Println(y.fu())
    fmt.Println(x.X)
    fmt.Println(y.X)
    var z C
    z = &x
    fmt.Println(z.fu())
    z = &y
    fmt.Println(z.fu())
    fmt.Println(z.G().X)
    fmt.Println(z.G().fu())
}
package main

import "fmt"

type A struct {
    X string
}

func (a *A) Set(x string) {
    a.X = x
}

func (a *A) Get() string {
    return a.X
}

type B interface {
    Set(string)
    Get() string
}

func main() {
    a := &A{X: "hehe"}
    var b, c B
    b = a
    c = b
    fmt.Println(a.Get())
    fmt.Println(b.Get())
    fmt.Println(c.Get())
    c.Set("gg")
    fmt.Println(a.Get())
    fmt.Println(b.Get())
    fmt.Println(c.Get())
}
/*
hehe
hehe
hehe
gg
gg
gg
*/
  • go后面跟的参数都会在当前协程算,调用的函数才是会新开一个协程执行
  • chan int是可读可写的int阻塞消息队列;chan <- int是只写的;<- chan int是只读的
  • select你可以认为是for + switch的死循环
  • context介绍1介绍2
  • go routine常用例子
ch := make(chan Data, numberOfGoroutine)
wg := sync.WaitGroup{}
wg.Add(numberOfGoroutine)

for gid := 0; gid < numberOfGoroutine; gid++ {
    go func() {
        for {
            data, ok := <- ch
            if !ok {
                wg.Done()
                return
            }
            // Do something
        }
    }()
}
// send data to ch
close(ch)
wg.Wait()

测试

参见别人的文章

禁止 cache

$ go test -count=1 

CI

language: go

go:
- "1.6"
- "1.7"
- "1.8"
- "1.9"
- "1.10"
- "1.11"
- "1.12"
- "master"

script:
  - cd example && go build ./...

交叉编译

可以看别人的文章

你大概可以写一个这样的脚本

#!/bin/bash

v="$1"

CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o ./cf${v}_win-amd64/cf.exe ../cf.go
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o ./cf${v}_darwin-amd64/cf ../cf.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o ./cf${v}_linux-amd64/cf ../cf.go

注释规范

参见别人的文章

包管理

Go是不自带包管理工具的。。也就是包的版本是无法写死的,这就会导致很不稳定的问题。不过Go官方出了一个拓展dep来解决这个问题。中文使用教程

一些好用的库

大全1 hackstoic/golang-open-source-projects
大全2 avelino/awesome-go

测试

Web框架

数据库

监控系统

日志记录

数据合法性验证

性能优化

欢迎留言>_<

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据