GO語言語法

Go語言語法

順序編程

變量

var v10 int
v10 = 123

v11 := 234

// 匿名變量示例
func GetName() (firstName, lastName nickName string) {
  return "May", "Chan", "Chibi"
}
_, _, nickName := GetName()

常量

iota是一個(gè)特殊的常量,它會(huì)在每一個(gè)const關(guān)鍵字出現(xiàn)時(shí)重置為0,然后在下一個(gè)const出現(xiàn)前,每出現(xiàn)一次iota,它代表的值加1。如果兩個(gè)const的賦值語句表達(dá)式一樣,后一個(gè)賦值可省略。

const (
  c0 = iota
  c1 = iota
  c2
)

枚舉

const (
  Sunday = iota
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  numberOfDays
)

類型

  • 布爾:bool
  • 整型:int8、byte、int16、int、uint、unitptr等
  • 浮點(diǎn):float32、float64
  • 復(fù)數(shù):complex64、complex128
  • 字符串:string:在初始化后不能被修改
  • 字符:rune
  • 錯(cuò)誤:error
  • 指針(pointer)
  • 數(shù)組(array)
  • 切片(slice):可動(dòng)態(tài)增減元素
  • 字典(map)
  • 通道(chan)
  • 接口(interface)
  • 結(jié)構(gòu)體(struct)
// 切片例子
mySlice := make([]int, 5, 10)

fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))

mySlice = append(mySlice, 1, 2, 3)

mySlice2 := []int{8, 9, 10}
mySlice = append(mySlice, mySlice2)

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}

copy(slice2, slice1) //將slice1中的前3個(gè)元素復(fù)制到slice2
copy(slice1, slice2) //將slice2中的前3個(gè)元素復(fù)制到slice1
// Map例子
package main
import (
    "fmt"
)

type PersonInfo struct{
    ID string
    Name string
    Address string
}

func main(){
    var personDB map[string] PersonInfo
    personDB = make(map[string] PersonInfo)

    personDB["12345"] = PersonInfo{"12345", "Tom", "Room 12345"}
    personDB["546"] = PersonInfo{"546", "Tom", "Room 546"}

    person, ok := personDB["1234"]

    if ok {
        fmt.Println("Found person", person.Name)
    }else{
        fmt.Println("Did not find person")
    }
    delete(personDB, "546")
    fmt.Println(personDB["12345"].Name)
}

流程控制

// if else
func if_else(x int) int {
    if x == 0 {
        return 5
    }else{
        return x
    }
}

// switch case
func switch_case(i int){ //無需break
    switch i{
    case 0:
        fmt.Println("0")
    case 1:
        fmt.Println("1")
    default:
        fmt.Println("Default")
    }
}

// for
sum := 0
for {
    sum ++
    if sum > 10 {
        break
    }
}

for j := 0; j < 5; j++ {
    ftm.Println(j)
}

// goto
func myfunc(){
    i := 0
    HERE:
    fmt.Println(i)
    i++
    if i < 10 {
        goto HERE
    }
}

函數(shù)

import "mymath"  //假設(shè)Add被放在一個(gè)叫mymath的包中
c := mymath.Add(1, 2)

小寫字母開頭的函數(shù)只在本包內(nèi)可見,大寫字母開頭的函數(shù)才能被其他包使用。

func myfunc(args ...int) {
  for _, arg := range args {
    fmt.Println(arg)
} 
}

func myfunc(args ...int) {
    for _, arg := range args {
        fmt.Println(arg)
    }
    myfunc2(args...)
}

任意類型的不定參數(shù)

func Printf(format string, args ...interface{}){
    for _, arg := range args {
        switch arg.(type) {
            case int:
                fmt.Println(arg, "is an int value")
            case string:
                fmt.Println(arg, "is a string value")
            case int64:
                fmt.Println(arg, "is an int64 value")
            default:
                fmt.Println(arg, "is an unknown type")
        }
    }
}

多返回值

func (file *File) Read(b []byte) (n int, err Error)

匿名函數(shù)與閉包

package main
import(
    "fmt"
)
func main(){
    var j int = 5
    a := func()(func()){
        var i int = 10
        return func() {
            fmt.Printf("i, j: %d, %d\n", i, j)
        }
    }()
    a()
    j *= 2
    a()
}
// 結(jié)果
// i, j: 10, 5
// i, j: 10, 10

異常處理

type error interface {
  Error() string
}

大多數(shù)函數(shù),如果要返回錯(cuò)誤,可以定義如下模式。

func Foo(param int)(n int, err error) {
  // ...
}

n, err := Foo(0)
if err != nil {
  // 錯(cuò)誤處理
} else {
  // 使用返回值n
}

type PathError struct {
  Op string
  Path string
  Err  error
}
func (e *PathError) Error() string {
  return e.Op + " " + e.Path + ": " + e.Err.Error()
}

defer

func CopyFile(dst, src string) (w int64, err error){
    srcFile, err := os.Open(src)
    if err != nil {
        return
    }
    defer srcFile.Close()

    dstFile, err := os.Create(dst)
    if err != nil {
        return
    }
    defer dstFile.Close()

    return io.Copy(dstFile, srcFile)
}

panic/recover

defer func() {
    if r := recover(); r != nil {
        log.Printf("Runtime error caught: %v", r)
    }
}()

面向?qū)ο缶幊?/h1>

結(jié)構(gòu)體

type Integer int
func (a Integer) Less(b Integer) bool {
  return a < b
}
type Header map[string] []string
func (h Header) Add(key, value string){
  textproto.MIMEHeader(h).Add(key, value)
}
func (h Header) Set(key, value string){
  textproto.MIMEHeader(h).Set(key, value)
}

引用類型:數(shù)組切片、map、channel、接口(interface)
值類型:byte、int、bool、float32、float64、string、array、struct、pointer等

type Animal struct {
    Name   string  //名稱
    Color  string  //顏色
    Height float32 //身高
    Weight float32 //體重
    Age    int     //年齡
}
//奔跑
func (a Animal)Run() {
    fmt.Println(a.Name + "is running")
}
type Lion struct {
    Animal //匿名字段
}

func main(){
    var lion = Lion{
        Animal{
            Name:  "小獅子",
            Color: "灰色",
        },
    }
    lion.Run()
    fmt.Println(lion.Name)
}

匿名組合相當(dāng)于以其類型名稱(去掉包名部分)作為成員變量的名字。
Go語言中,要使某個(gè)符號(hào)對(duì)其他包可見,需要將該符號(hào)定義為以大寫字母開頭。Go語言中符號(hào)的可訪問性是包一級(jí)別,而不是類型級(jí)別。

接口

Go語言中,一個(gè)類只需要實(shí)現(xiàn)了接口要求的所有函數(shù),就說這個(gè)類實(shí)現(xiàn)了該接口。

type File struct {
  // ...
}
func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Close() error

type IFile interface{
  IReader
  IWrite
  IClose
}
type IReader interface{
  Read(buf []byte) (n int, err error)
}
type IWriter interface{
  Write(buf []byte) (n int, err error)
}
type IClose interface{
  Close() error
}

var file1 IFile = new(File)
var file2 IReader = new(File)

Go語言中任何對(duì)象實(shí)例都滿足空接口interface{}。

并發(fā)編程

go channel

package main
import "fmt"

func Count(ch chan int){
    fmt.Println("Counting")
    ch <- 1
}

func main(){
    chs := make([]chan int, 10)
    for i :=0; i < 10; i++ {
        chs[i] = make(chan int)
        go Count(chs[i])
    }

    for _, ch := range(chs) {
        a := <- ch
        fmt.Println(a)
    }
}

超時(shí)機(jī)制

timeout := make(chan bool, 1)
go func(){
    time.Sleep(1e9)
    timeout <- true
}()

select {
case <-ch:
    //從ch中讀取到數(shù)據(jù)
case <-timeout
    //一直沒有從ch中讀取到數(shù)據(jù),但從timeout中讀取到了數(shù)據(jù)
}

channel的傳遞


type PipeData struct {
    value int
    handler func(int) int
    next chan int
}

func handle(queue chan *PipeData){
    for data := range queue{
        data.next <- data.handler(data.value)
    }
}

關(guān)閉channel

close(ch)

同步鎖

var l sync.Mutex
func foo(){
    l.Lock()
    defer l.Unlock()
    //...
}

全局唯一操作

var a string
var once sync.once
func setup(){
    a = "hello"
}

func doprint() {
    once.Do(setup)
    print(a)
}

func twoprint(){
    go doprint()
    go doprint()
}

以上代碼setup只會(huì)運(yùn)行一次。

網(wǎng)絡(luò)編程

Socket編程

Dial():連接,支持如下協(xié)議:tcp,tcp4,tcp6,udp,udp4,udp6,ip,ip4,ip6

conn, err := net.Dial("tcp", "192.168.0.2:2100")
conn2, err := net.Dial("udp", "192.168.0.2:53")
conn3, err := net.Dial("ip4:icmp", "www.baidu.com")

Write()與Read()方法來發(fā)送數(shù)據(jù)與接收數(shù)據(jù)。

HTTP編程

net/http包,包含HTTP客戶端與服務(wù)端的具體實(shí)現(xiàn)。

resp, err := http.Get("http://example.com/")
if err != nil{
    return
}
defer resp.Body.close()
io.Copy(os.Stdout, resp.Body)
resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil{
    return
}
if resp.StatusCode != http.StatusOK{
    return
}
resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"artical body"}})
if err != nil{
    return
}
resp, err := http.Head("http://example.com/")
req, err := http.NewRequest("GET", "http://example.com/posts", nil)
req.Header.Add("User-Agent", "Gobook User-Agent")

client := &http.Client{...}
resp, err := client.Do(req)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){
  fmt.Fprintf(w, "hello")
})

RPC

net/rpc,包實(shí)現(xiàn)RPC協(xié)議的細(xì)節(jié)

func (t *T) MethodName(argType T1, replyType *T2) error

T1與T2默認(rèn)會(huì)使用encoding/gob包進(jìn)行編碼與解碼。
argType表示由RPC客戶端傳入的參數(shù)
replyType表示要返回給RPC客戶端的結(jié)果
服務(wù)端示例

package main
import (
    "log"
    "net"
    "net/http"
    "net/rpc"
)
type Args struct{
    A, B int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error{
    *reply = args.A * args.B
    return nil
}

func main(){
    arith := new(Arith)
    rpc.Register(arith)
    rpc.HandleHTTP()
    l, e := net.Listen("tcp", ":1234")
    if e != nil{
        log.Fatal("listen error", e)
    }
    http.Serve(l, nil)
}

客戶端示例

package main
import (
    "net/rpc"
    "fmt"
    "log"
)

type Args struct{
    A, B int
}

func main(){
// 順序執(zhí)行RPC
    client, _ := rpc.DialHTTP("tcp", "127.0.0.1:1234")
    args := &Args{7,8}
    var reply int
    err := client.Call("Arith.Multiply", args, &reply)
    if err != nil{
        log.Fatal(err)
    }
    fmt.Println(reply)

//異步執(zhí)行RPC
        var reply2 int
    divCall := client.Go("Arith.Multiply", &Args{6,8}, &reply2, nil)
    divCall = <-divCall.Done
    fmt.Println(reply2)
}

JSON

encoding/json標(biāo)準(zhǔn)庫(kù)
json.Marshal(),將一組數(shù)據(jù)進(jìn)行JSON格式化編碼
json.Unmarshal(),將JSON格式的文檔解碼為Go里邊預(yù)期的數(shù)據(jù)結(jié)構(gòu)。

data := make(map[string] string)
data["name"] = "Michael"
data2, err := json.Marshal(data)
if err != nil{
    fmt.Println("err")
    return
}
fmt.Println(string(data2[:]))
data := make(map[string] string)
data_json := []byte("{\"name\":\"Michael\"}")

json.Unmarshal(data_json, &data)
fmt.Println(data)

反射

type T struct{
    A int
    B string
}
t := T{203, "mh203"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()

for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}

Go工具

包文檔查看(網(wǎng)站形式)

$ godoc -http=:8080
$ godoc -http=:8080 -path="."

包文檔查看(命令行形式)

$ go doc foo

代碼格式化

$ go fmt hello.go
$ gofmt -l -w .

項(xiàng)目構(gòu)建
go build:用于測(cè)試編譯包,在項(xiàng)目目錄下生成可執(zhí)行文件(有main包);不能生成包文件
go install:主要用來生成庫(kù)和工具。一是編譯包文件(無main包),將編譯后的包文件放到 pkg 目錄下($GOPATH/pkg)。二是編譯生成可執(zhí)行文件(有main包),將可執(zhí)行文件放到 bin 目錄($GOPATH/bin);生成可執(zhí)行文件在當(dāng)前目錄下, go install 生成可執(zhí)行文件在bin目錄下($GOPATH/bin)

$ GOOS=linux GOARCH=amd64 go build calc 
$ go install calc

GOOS:系統(tǒng),linux/darwin/windows
GOARCH:386/amd64

單元測(cè)試

$ go test calc

模塊管理go mod

$ go mod download  # 下載指定模塊
$ go mod init test.com/go  #初始化當(dāng)前模塊
$ go mod vendor       # 將依賴復(fù)制到當(dāng)前vendor目錄下

使用go module后,項(xiàng)目代碼不需要放在GOPATH目錄下。import項(xiàng)目的package時(shí)使用module路徑。

以下是beego應(yīng)用使用module來管理依賴的例子

$ cat <<EOF > go.mod
module beegoapp

go 1.12

replace (
    golang.org/x/crypto => github.com/golang/crypto v0.0.0-20191227163750-53104e6ec876
    golang.org/x/mod => github.com/golang/mod v0.1.0
    golang.org/x/net => github.com/golang/net v0.0.0-20191209160850-c0dbc17a3553
    golang.org/x/sync => github.com/golang/sync v0.0.0-20190911185100-cd5d95a43a6e
    golang.org/x/sys => github.com/golang/sys v0.0.0-20191228213918-04cbcbbfeed8
    golang.org/x/text => github.com/golang/text v0.3.2
    golang.org/x/tools => github.com/golang/tools v0.0.0-20191230220329-2aa90c603ae3
    golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20191204190536-9bdfabe68543
)

require (
    github.com/astaxie/beego v1.12.0
    github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
    github.com/smartystreets/goconvey v1.6.4
)

EOF

GO的GC規(guī)則

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容