Contents

15 Minute Crash Golang

Contents

The project needs to go. Translated a great golang tutorial. The most distinctive part of the original text is that it explains the code in the way of code, and explains the characteristics of the language in the way of code. It is very suitable for people with a certain foundation to see. What is this called, ** Eschew surplusage **

The original translation is very machine translation, and the content is not complete - I still think that people who write code should read more books, really! Even if it is not for girls, the document must always be written clearly.

Original

Golang was invented out of a need to do a better job. Go is not the latest trend in computer science, but it provides the latest and fastest ways to solve real-world problems.

Go has the static type of imperative language, which compiles quickly and executes quickly. At the same time, it adds concurrent computing support for current multi-core CPUs, and also has corresponding features to realize large-scale programming.

Golang has a great standard library and a passionate community.

// 单行注释
/* 多行
    注释 */

// 导入包的子句在每个源文件的开头。
// Main比较特殊,它用来声明可执行文件,而不是一个库。
package main

// Import语句声明了当前文件引用的包。
import (
    "fmt"       // Go语言标准库中的包
    "io/ioutil" // 包含一些输入输出函数
    m "math"    // 数学标准库,在此文件中别名为m
    "net/http"  // 一个web服务器包
    "os"        // 系统底层函数,如文件读写
    "strconv"   // 字符串转换
)

// 函数声明:Main是程序执行的入口。
// 不管你喜欢还是不喜欢,反正Go就用了花括号来包住函数体。
func main() {
    // 往标准输出打印一行。
    // 用包名fmt限制打印函数。
    fmt.Println("Hello world!")

    // 调用当前包的另一个函数。
    beyondHello()
}

// 函数可以在括号里加参数。
// 如果没有参数的话,也需要一个空括号。
func beyondHello() {
    var x int   // 变量声明,变量必须在使用之前声明。
    x = 3       // 变量赋值。
    // 可以用:=来偷懒,它自动把变量类型、声明和赋值都搞定了。
    y := 4
    sum, prod := learnMultiple(x, y)        // 返回多个变量的函数
    fmt.Println("sum:", sum, "prod:", prod) // 简单输出
    learnTypes()                            // 少于y分钟,学的更多!
}

/* <- 快看快看我是跨行注释_(:з」∠)_
Go语言的函数可以有多个参数和 *多个* 返回值。
在这个函数中, `x`、`y` 是参数,
`sum`、`prod` 是返回值的标识符(可以理解为名字)且类型为int
*/
func learnMultiple(x, y int) (sum, prod int) {
    return x + y, x * y // 返回两个值
}

// 内置变量类型和关键词
func learnTypes() {
    // 短声明给你所想。
    str := "Learn Go!" // String类型

    s2 := `A "raw" string literal
can include line breaks.` // 同样是String类型

    // 非ascii字符。Go使用UTF-8编码。
    g := 'Σ' // rune类型,int32的别名,使用UTF-8编码

    f := 3.14195 // float64类型,IEEE-754 64位浮点数
    c := 3 + 4i  // complex128类型,内部使用两个float64表示

    // Var变量可以直接初始化。
    var u uint = 7  // unsigned 无符号变量,但是实现依赖int型变量的长度
    var pi float32 = 22. / 7

    // 字符转换
    n := byte('\n') // byte是uint8的别名

    // 数组(Array)类型的大小在编译时即确定
    var a4 [4] int              // 有4个int变量的数组,初始为0
    a3 := [...]int{3, 1, 5}     // 有3个int变量的数组,同时进行了初始化

    // Array和slice各有所长,但是slice可以动态的增删,所以更多时候还是使用slice。
    s3 := []int{4, 5, 9}    // 回去看看 a3 ,是不是这里没有省略号?
    s4 := make([]int, 4)    // 分配4个int大小的内存并初始化为0
    var d2 [][]float64      // 这里只是声明,并未分配内存空间
    bs := []byte("a slice") // 进行类型转换

    // 切片(Slice)的大小是动态的,它的长度可以按需增长
    // 用内置函数 append() 向切片末尾添加元素
    // 要增添到的目标是 append 函数第一个参数,
    // 多数时候数组在原内存处顺次增长,如
    s := []int{1, 2, 3}     // 这是个长度3的slice
    s = append(s, 4, 5, 6)  // 再加仨元素,长度变为6了
    fmt.Println(s) // 更新后的数组是 [1 2 3 4 5 6]

    // 除了向append()提供一组原子元素(写死在代码里的)以外,我们
    // 还可以用如下方法传递一个slice常量或变量,并在后面加上省略号,
    // 用以表示我们将引用一个slice、解包其中的元素并将其添加到s数组末尾。
    s = append(s, []int{7, 8, 9}...) // 第二个参数是一个slice常量
    fmt.Println(s)  // 更新后的数组是 [1 2 3 4 5 6 7 8 9]

    p, q := learnMemory()       // 声明p,q为int型变量的指针
    fmt.Println(*p, *q)         // * 取值

    // Map是动态可增长关联数组,和其他语言中的hash或者字典相似。
    m := map[string]int{"three": 3, "four": 4}
    m["one"] = 1

    // 在Go语言中未使用的变量在编译的时候会报错,而不是warning。
    // 下划线 _ 可以使你“使用”一个变量,但是丢弃它的值。
    _, _, _, _, _, _, _, _, _, _ = str, s2, g, f, u, pi, n, a3, s4, bs
    // 通常的用法是,在调用拥有多个返回值的函数时,
    // 用下划线抛弃其中的一个参数。下面的例子就是一个脏套路,
    // 调用os.Create并用下划线变量扔掉它的错误代码。
    // 因为我们觉得这个文件一定会成功创建。
    file, _ := os.Create("output.txt")
    fmt.Fprint(file, "This is how you write to a file, by the way")
    file.Close()

    // 输出变量
    fmt.Println(s, c, a4, s3, d2, m)

    learnFlowControl() // 回到流程控制
}

// 和其他编程语言不同的是,go支持有名称的变量返回值。
// 声明返回值时带上一个名字允许我们在函数内的不同位置
// 只用写return一个词就能将函数内指定名称的变量返回
func learnNamedReturns(x, y int) (z int) {
    z = x * y
    return // z is implicit here, because we named it earlier.

// Go全面支持垃圾回收。Go有指针,但是不支持指针运算。
// 你会因为空指针而犯错,但是不会因为增加指针而犯错。
func learnMemory() (p, q *int) {
    // 返回int型变量指针p和q
    p = new(int)    // 内置函数new分配内存
    // 自动将分配的int赋值0,p不再是空的了。
    s := make([]int, 20)    // 给20个int变量分配一块内存
    s[3] = 7                // 赋值
    r := -2                 // 声明另一个局部变量
    return &s[3], &r        // & 取地址
}

func expensiveComputation() int {
    return 1e6
}

func learnFlowControl() {
    // If需要花括号,括号就免了
    if true {
        fmt.Println("told ya")
    }
    // 用go fmt 命令可以帮你格式化代码,所以不用怕被人吐槽代码风格了,
    // 也不用容忍别人的代码风格。
    if false {
        // pout
    } else {
        // gloat
    }
    // 如果太多嵌套的if语句,推荐使用switch
    x := 1
    switch x {
    case 0:
    case 1:
        // 隐式调用break语句,匹配上一个即停止
    case 2:
        // 不会运行
    }
    // 和if一样,for也不用括号
    for x := 0; x < 3; x++ { // ++ 自增
        fmt.Println("iteration", x)
    }
    // x在这里还是1。为什么?

    // for 是go里唯一的循环关键字,不过它有很多变种
    for { // 死循环
        break    // 骗你的
        continue // 不会运行的
    }

    // 用range可以枚举 array、slice、string、map、channel等不同类型
    // 对于channel,range返回一个值,
    // array、slice、string、map等其他类型返回一对儿
    for key, value := range map[string]int{"one": 1, "two": 2, "three": 3} {
        // 打印map中的每一个键值对
        fmt.Printf("key=%s, value=%d\n", key, value)
    }
    // 如果你只想要值,那就用前面讲的下划线扔掉没用的
    for _, name := range []string{"Bob", "Bill", "Joe"} {
        fmt.Printf("Hello, %s\n", name)
    }

    // 和for一样,if中的:=先给y赋值,然后再和x作比较。
    if y := expensiveComputation(); y > x {
        x = y
    }
    // 闭包函数
    xBig := func() bool {
        return x > 100 // x是上面声明的变量引用
    }
    fmt.Println("xBig:", xBig()) // true (上面把y赋给x了)
    x /= 1e5                     // x变成10
    fmt.Println("xBig:", xBig()) // 现在是false

    // 除此之外,函数体可以在其他函数中定义并调用,
    // 满足下列条件时,也可以作为参数传递给其他函数:
    //   a) 定义的函数被立即调用
    //   b) 函数返回值符合调用者对类型的要求
    fmt.Println("Add + double two numbers: ",
        func(a, b int) int {
            return (a + b) * 2
        }(10, 2)) // Called with args 10 and 2
    // => Add + double two numbers: 24

    // 当你需要goto的时候,你会爱死它的!
    goto love
love:

    learnFunctionFactory() // 返回函数的函数多棒啊
    learnDefer()      // 对defer关键字的简单介绍
    learnInterfaces() // 好东西来了!
}

func learnFunctionFactory() {
    // 空行分割的两个写法是相同的,不过第二个写法比较实用
    fmt.Println(sentenceFactory("summer")("A beautiful", "day!"))

    d := sentenceFactory("summer")
    fmt.Println(d("A beautiful", "day!"))
    fmt.Println(d("A lazy", "afternoon!"))
}

// Decorator在一些语言中很常见,在go语言中,
// 接受参数作为其定义的一部分的函数是修饰符的替代品
func sentenceFactory(mystring string) func(before, after string) string {
    return func(before, after string) string {
        return fmt.Sprintf("%s %s %s", before, mystring, after) // new string
    }
}

func learnDefer() (ok bool) {
    // defer表达式在函数返回的前一刻执行
    defer fmt.Println("deferred statements execute in reverse (LIFO) order.")
    defer fmt.Println("\nThis line is being printed first because")
    // 关于defer的用法,例如用defer关闭一个文件,
    // 就可以让关闭操作与打开操作的代码更近一些
    return true
}

// 定义Stringer为一个接口类型,有一个方法String
type Stringer interface {
    String() string
}

// 定义pair为一个结构体,有x和y两个int型变量。
type pair struct {
    x, y int
}

// 定义pair类型的方法,实现Stringer接口。
func (p pair) String() string { // p被叫做“接收器”
    // Sprintf是fmt包中的另一个公有函数。
    // 用 . 调用p中的元素。
    return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func learnInterfaces() {
    // 花括号用来定义结构体变量,:=在这里将一个结构体变量赋值给p。
    p := pair{3, 4}
    fmt.Println(p.String()) // 调用pair类型p的String方法
    var i Stringer          // 声明i为Stringer接口类型
    i = p                   // 有效!因为p实现了Stringer接口(类似java中的塑型)
    // 调用i的String方法,输出和上面一样
    fmt.Println(i.String())

    // fmt包中的Println函数向对象要它们的string输出,实现了String方法就可以这样使用了。
    // (类似java中的序列化)
    fmt.Println(p) // 输出和上面一样,自动调用String函数。
    fmt.Println(i) // 输出和上面一样。

    learnVariadicParams("great", "learning", "here!")
}

// 有变长参数列表的函数
func learnVariadicParams(myStrings ...interface{}) {
    // 枚举变长参数列表的每个参数值
    // 下划线在这里用来抛弃枚举时返回的数组索引值
    for _, param := range myStrings {
        fmt.Println("param:", param)
    }

    // 将可变参数列表作为其他函数的参数列表
    fmt.Println("params:", fmt.Sprintln(myStrings...))

    learnErrorHandling()
}

func learnErrorHandling() {
    // ", ok"用来判断有没有正常工作
    m := map[int]string{3: "three", 4: "four"}
    if x, ok := m[1]; !ok { // ok 为false,因为m中没有1
        fmt.Println("no one there")
    } else {
        fmt.Print(x) // 如果x在map中的话,x就是那个值喽。
    }
    // 错误可不只是ok,它还可以给出关于问题的更多细节。
    if _, err := strconv.Atoi("non-int"); err != nil { // _ discards value
        // 输出"strconv.ParseInt: parsing "non-int": invalid syntax"
        fmt.Println(err)
    }
    // 待会再说接口吧。同时,
    learnConcurrency()
}

// c是channel类型,一个并发安全的通信对象。
func inc(i int, c chan int) {
    c <- i + 1 // <-把右边的发送到左边的channel。
}

// 我们将用inc函数来并发地增加一些数字。
func learnConcurrency() {
    // 用make来声明一个slice,make会分配和初始化slice,map和channel。
    c := make(chan int)
    // 用go关键字开始三个并发的goroutine,如果机器支持的话,还可能是并行执行。
    // 三个都被发送到同一个channel。
    go inc(0, c) // go is a statement that starts a new goroutine.
    go inc(10, c)
    go inc(-805, c)
    // 从channel中读取结果并打印。
    // 打印出什么东西是不可预知的。
    fmt.Println(<-c, <-c, <-c) // channel在右边的时候,<-是读操作。

    cs := make(chan string)       // 操作string的channel
    cc := make(chan chan string)  // 操作channel的channel
    go func() { c <- 84 }()       // 开始一个goroutine来发送一个新的数字
    go func() { cs <- "wordy" }() // 发送给cs
    // Select类似于switch,但是每个case包括一个channel操作。
    // 它随机选择一个准备好通讯的case。
    select {
    case i := <-c: // 从channel接收的值可以赋给其他变量
        fmt.Println("it's a", i)
    case <-cs: // 或者直接丢弃
        fmt.Println("it's a string")
    case <-cc: // 空的,还没作好通讯的准备
        fmt.Println("didn't happen.")
    }
    // 上面c或者cs的值被取到,其中一个goroutine结束,另外一个一直阻塞。

    learnWebProgramming() // Go很适合web编程,我知道你也想学!
}

// http包中的一个简单的函数就可以开启web服务器。
func learnWebProgramming() {
    // ListenAndServe第一个参数指定了监听端口,第二个参数是一个接口,特定是http.Handler。
    go func() {
        err := http.ListenAndServe(":8080", pair{})
        fmt.Println(err) // 不要无视错误。
    }()

    requestServer()
}

// 使pair实现http.Handler接口的ServeHTTP方法。
func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 使用http.ResponseWriter返回数据
    w.Write([]byte("You learned Go in Y minutes!"))
}

func requestServer() {
    resp, err := http.Get("http://localhost:8080")
    fmt.Println(err)
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Printf("\nWebserver said: `%s`", string(body))
}

Go further

Everything about Go you can find at < gt r = “2”/>. There you can get tutorial references, online trials, and more. After a simple attempt, at < gt r = “3”/> you will get all the information you need, documentation on writing code specifications, libraries and command line tools and Go version history.

Highly recommend reading the Language Definitions section, it’s easy and concise! (Be trendy!)

You can also go to < gt r = “4”/>, modify and run this code in the browser, be sure to give it a try! You can treat < gt r = “5”/> as a < gt r = “6”/>, where you can experience language features or run your own code without even matching the environment!

Learn Go also need to read Go < gt r = “7”/>, all documented, very readable, you can learn go, go style and go idioms. Click on the function name in < gt r = “8”/>, and the source code will come out!

< Gt r = “9”/> is also a great place to learn.

Go Mobile adds support for mobile platforms (Android and iOS). You can create an app entirely in go or write a library that can be called from Java or Obj-C, see < gt r = “10”/>.