"Effective Go" post-reading record

Source: Internet
Author: User
Tags vars
This is a creation in Article, where the information may have evolved or changed.

An online go compiler

If you haven't had time to install the go environment and want to experience the go language, you can run the Go program on the go online compiler.

Formatting

Getting everyone to follow the same coding style is an ideal, and now the Go language gofmt程序 allows the machine to handle most of the formatting problems. The gofmt程序 Go Standard library provides a program that you can try to run, which indents, aligns, and preserves annotations in standard style, which is indented by default with tabs. All the code in the Go standard library is gofmt程序 formatted.

Comments

The GO comment supports C-style block comments /* */ and C + + style line annotations // . Block annotations are primarily used as comments for packages. Go officially advocates that each package should contain a package note, which is a block note placed before the bun sentence. For packages with multiple files, package annotations only need to appear in one of the files.

Godoc is both a program and a WEB server that processes the source code of Go and extracts the contents of the document in the package. A comment that appears before the top-level declaration and there is no blank line between the declaration is extracted with the declaration as a descriptive document for the entry.

Named

    • The naming of the Go language affects semantics: whether a name is visible outside the package depends on whether its first character is uppercase.
    • Package: You should name a single word in lowercase, and you should not use an underscore or hump notation.
    • Package name: should be the base name of its source directory, for example, the package in src/pkg/encoding/base64 should be imported as "encoding/base64" and its package name should be base64
    • The picker: If there is a field named owner (lowercase, not exported), its picker should be named owner (uppercase, exportable) instead of GetOwner. To provide a set method, you can select SetOwner.
    • Interface: An interface that contains only one method should be named with the name of the method plus the-er suffix
    • Hump notation: Go in the convention using hump notation Mixedcaps or Mixedcaps

Semicolon

    • The lexical analyzer for go uses simple rules to automatically insert semicolons
    • If you write multiple statements on a single line, you need to separate them with semicolons
    • The left curly brace of the control structure cannot be placed on the next line because, based on the rules of the lexical parser, a semicolon is added before the curly braces, causing an error

Initialization

constants must be initialized at the time they are defined . Constants can only be basic types such as numbers, characters, strings, Boolean values, and the expressions that define them must be types that can be evaluated at compile time. Use const to define a constant:

  const LENGTH int = 10  

In Go, 枚举常量 used iota to create, iota is a self-growing value:

type AudioOutput intconst (    OutMute AudioOutput = iota // 0    OutMono                    // 1    OutStereo                  // 2    _    _    OutSurround                // 5)

iotais always used for increment, but it can also be used for expressions, and in effective Go shows a definition of the magnitude of the representation:

type ByteSize float64const (    _          = iota                  // 使用_来忽略iota=0    KB ByteSize = 1 << (10 * iota) // 1 << (10*1)    MB                                  // 1 << (10*2)    GB                                  // 1 << (10*3)    TB                                  // 1 << (10*4)    PB                                  // 1 << (10*5)    EB                                  // 1 << (10*6)    ZB                                  // 1 << (10*7)    YB                                  // 1 << (10*8))

The source file can be defined 无参数init函数 , which is called automatically before the function logic is actually executed, and the following program simply explains this:

package mainimport "fmt"func init() {    fmt.Print("执行init函数0\n")}func init() {    fmt.Print("执行init函数1\n")}func init() {    fmt.Print("执行init函数2\n")}func main() {    fmt.Print("执行main函数\n")}//output :执行init函数0执行init函数1执行init函数2执行main函数

As you can see, before executing the logic in the main function, the INIT function is called first, and multiple init functions can be defined in the same source file. The init function is often used to validate variables and program states before the program actually executes. Its execution mechanism is this:

    • Init is not called until all variables in the package are evaluated by the initializer
    • Init is then called after all imported packages have been initialized.

Control structure

go uses a more general for instead of do and while loops, for three forms:

// Like a C forfor init ; condition;post {  }//Like a C while for condition{  }//Like a C for(;;)for {}

For arrays, slices, strings, maps, or read messages from a channel, you can use range子句 :

for key ,value := range oldMap {    newMap[key] = value}

The switch to go is more versatile, switch it will match when there is no expression, ture which also means that the if-else-if-else chain can be used switch to implement:

func unhex(c byte) byte {    switch { //switch将匹配true    case '0' <= c && c <= '9':        return c - '0'    case 'a' <= c && c <= 'f':        return c - 'a' + 10    case 'A' <= c && c <= 'F':        return c - 'A' + 10    }    return 0}

Function

The function of go can be returned in multiple values . This clumsy usage is often used in the C language: The function tells the function by its return value, such as returning 0 for no exception, 1 for the return, and EOF so on, while passing the data to the outside through the pointer argument. It is now possible to solve this problem by using the Go function's multi-value return. Here is the signature of the open file in the Go standard library File.Write :

func (file *File) Write(b []byteinterror)

WriteThe function returns the number of bytes written and an error. If it is written correctly, err it nil is, otherwise, err a non nil - error error value, which is a common coding style in go.

The return value of the Go function can be named . The return value of Go can be used as a regular variable in the function body, known as the 结果“形参” 结果“形参” 0 value corresponding to its type when the function starts executing. If the function executes without parameters, the return current value of the result parameter is returned:

funcintint){    if0{        result = -i   //返回值result可以直接当成常规变量使用    }    return }

The advantage of this is that the signature of the function is the document, and the meaning of the return value is also written in the function signature, which improves the readability of the code.

GO provides defer statements for delaying the execution of functions . A defer statement-decorated function that is called before the outer-layer function ends. You can use this to defer语句 :

func printStr (a string){    fmt.Print(a);}func main() {    defer printStr("one\n")    defer printStr("two\n")    defer printStr("three\n")    fmt.Print("main()\n")}//output :main()threetwoone

About defer语句 :

    • Applies to closing open files, avoiding multiple return paths that need to be closed.
    • The arguments for a function that is deferred are evaluated when execution is deferred, rather than when the execution is invoked.
    • Deferred functions are executed in last in, first out (LIFO) order.
    • The defer statement is at the function level, and even if it is written in curly braces (blocks), the deferred function is only invoked at the end of the calling function.

There defer语句 are some details to keep in mind when using. The following code:

func main() {    fmt.Print(test())}func test() (r int) {    defer func() {        r = 1        return    }()    r = 2    return 3}//output:1

The output is not 3 , but 1 . The reason is that return the operation actually includes:

r = 0 //结果“形参”在函数开始执行时被初始化为零值r = 2 r = 1 //defer语句执行return r

Memory allocation

Go provides two distribution primitives new with make :

func new(Type) *Typefunc make(t Type, size ...IntegerType) Type

new(T)Used to allocate memory, it returns a pointer to the newly assigned 0 value of type T, and new the memory that is requested by the application is zeroed. This means that if a data structure is designed, then the 0 value of each type does not have to be further initialized.

make(T,args)is different from new(T) , it is only used to create slices (slice), map (map), channels (channel), these three types are essentially reference data types that must be initialized before they are used. The make return type is an T initialized value of type one, not *T .

Here's new the make comparison:

var p *[]int = new([]int) // 分配切片结构;*p == nil;基本没用var v []int = make([]int, 100) // 切片 v 现在引用了一个具有 100 个 int 元素的新数组// 没必要的复杂:var p *[]int = new([]int)*p = make([]int, 100, 100)// 习惯用法:v := make([]int, 100)

Array

The go array differs greatly from the C language array:

    • An array is a value that passes an array to a function, and the function gets a copy of the array, not a pointer.
    • The size of the array is part of the type. [10]intthere [20]int are two types of.

If you want to pass an array pointer like the C language, you need to do this:

func Sum(a *[3]float64) (sum float64) {for _, v := range *a {sum += v} return} array := [...]float64{7.0, 8.5, 9.1}x := Sum(&array) // 注意显式的取址操作

But this is not usually done in go, but rather through slicing to implement reference delivery. A slice holds a reference to the underlying array, and if you assign a slice to another slice, they reference the same array.

Slice

A slice is a small object that abstracts the underlying array and provides an appropriate way to do so, with the slice containing 3 fields whose internal implementation is:

You can define slices in some way:

var slice0 []type   //通过声明一个未指定大小的数组来定义切片var slice1 []type = make([]type, len) //通过make来创建切片,长度与容量都是5个元素make([]T, length, capacity) //可以通过make来指定它的容量

At the time of declaration, whenever [] a value is specified in the operator, the array is created instead of the slice, and the slice is created only when no value is specified.
A slice is called a slice because creating a new slice is a partial cut out of the underlying array, such as code:

slice := [] int {10,20,30,40,50} //创建一个切片,长度与容量都是5newSlice := slice[1:3] //创建一个新切片,其长度为5,容量为4

For new slices with the underlying array capacity K, the slice[i,j] length is, the j-i capacity is k-i , and the new tiles created are internally implemented as:

Since the two slices share a portion of the underlying array, modifying the 2nd element of Newslice will also modify the third element of slice .
You can use append to grow the length of a slice, in two cases:

    • When the available capacity of the slice is sufficient, the APPEND function increases the length of the slice without changing the capacity
    • When the available capacity of the tiles is insufficient, the APPEND function increases the capacity of the tiles, and the increase is that the capacity is multiplied by the tile capacity of less than 1000, and once the number of elements exceeds 1000, the capacity increase factor is 1.25, which increases by 25% at a time.

When append the function causes tile capacity expansion, the slice will have a completely new underlying array.

Mapping

A map is the same as a slice and is a reference type. If obtained through a nonexistent key value , the 0 value corresponding to the type of the item in the map is returned:

    var map1  map[string] int    map1 = make(map[string]int ,10)    map1["one"]=1    map1["two"]=2    fmt.Print(map1["three"])//output:     0

If it map1["three"] 's value exactly 0, how do you differentiate it? Can be used 多重赋值 in the form to distinguish this situation:

    i, ret := map1["three"]    if ret == true{        fmt.Print("map1[\"three\"]存在,值为:", i)        } else {        fmt.Print("map1[\"three\"] 不存在\n")    }

Or better to write, "effective Go," called the “comma ok” idiom the comma OK idiom

    if i, ret := map1["three"] ;ret {        fmt.Print("map1[\"three\"]存在,值为:", i)    } else {        fmt.Print("map1[\"three\"] 不存在\n")    }

If you only need to determine if a key exists, you can use it 空白标识符_ instead value :

    if _, ret := map1["three"] ;ret {        fmt.Print("map1[\"three\"]存在\n")    } else {        fmt.Print("map1[\"three\"]不存在,值为:")    }

Use the built-in function delete function to delete a key-value pair, even if the corresponding key is not in the map, and the delete operation is also safe

Method

In the section of the function, we have seen the write declaration of the function as:

func (file *File) Write(b []byte) (n int, err error)

We can abstract out the structure of functions in go:

func  [(p mytype)]  funcname([pram type]) [(parm type)] {//body}

Where the function is (p mytype) optional, the function with this part is called 方法(method) , this part is called 接收者(receiver) . We can define methods for any named type, including the type of struct that we define. By receiver , bind the method to the type. Here is an example:

package mainimport "fmt"//定义一个矩形类型type rect struct {    width ,height int}//这个方法扩大矩形边长为multiple倍//这个方法的reciever为*rect//表示这是定义在rect结构体上的方法func (r *rect) bigger(multiple int){    r.height *=multiple    r.height *=multiple}//方法的reciever可以为结构体类型//也可以为结构体指针类型//区别在于当reciever为类型指针时//可以在该方法内部修改结构体成员func (r rect) area() int{    return r.width*r.height}func main(){    r  := rect{width:10,height:5}    fmt.Print("r 's area:",r.area(),"\n")    r.bigger(10)    fmt.Print("r's area:",r.area())}//output:r 's area:50r's area:5000

The difference between a pointer or a value as a reciever is:

    • Pointers can modify the recipient
    • Value methods can be called by pointers and values, and pointer methods can only be called by pointers

The value method can be called by pointer and value, so the following statement is legal:

func main(){    r  := rect{width:10,height:5}        //通过指针调用    fmt.Print("r 's area:",(&r).area(),"\n")        //通过值调用    fmt.Print("r 's area:",r.area(),"\n")}//output:r 's area:50r 's area:50

For pointer methods that can only be invoked by pointers, you may be puzzled because the following statements are also valid:

func main(){    r  := rect{width:10,height:5}    fmt.Print("r 's area:",r.area(),"\n")        //通过值来调用指针方法(为什么合法?)    r.bigger(10)    fmt.Print("r's area:",r.area())}//output:

This is true: if the value is addressable, go will automatically insert the FETCH operator to deal with the generic pointer method that is called by value. In this case, r it is addressable and therefore will be r.Bigger(10) rewritten by the compiler (&r).Bigger .

In addition, the method can be "converted" into a function, which is not here to discuss.

Interface

Through methods and interfaces, the Go language defines a form of "inheritance" that is distinct from OOP languages such as java/c++. By implementing the method of the interface definition, you can assign the type variable of the reciever to the interface type variable, and invoke the method of the reciever type through the interface type variable, using C + + as an analogy, is to call the member functions of the derived class through the parent class pointer (although go does not have these concepts). The following is an example:

Package Mainimport ("FMT" "math")//Defines an interface geometry represents the geometry type geometry interface {area () float64 bigger (float 64)}//Rectangle and circle Two methods to implement this interface type rect struct {width, height float64}type circle struct {radius float64}//in Go, implement interface, only need to implement All methods defined by this interface can//rectangle interface method implement Func (R *rect) bigger (multiple float64) {r.height *= multiple r.height *= multiple}func (R * Rect) area () float64 {return r.width * r.height}//Circular interface method implementation Func (C *circle) bigger (multiple float64) {C.radius *= m Ultiple}func (c *circle) area () float64 {return math. Pi * C.radius * c.radius}//You can assign a variable of rect and circle type as an argument//to the Geometry interface type variable func measure (g geometry) {fmt. Print ("Geometry's Area:", G.area (), "\ n") G.bigger (2) fmt. Print ("After bigger 2 multiple, area:", G.area (), "\ n")}func main () {r: = Rect{width:10, height:5} c: = Circle{rad Ius:3} measure (&r) measure (&c)}//output:geometry ' s area:50after bigger 2 multiple, area:200geometry ' s is A:28.274333882308138after Bigger 2 multiple, area:113.09733552923255 

Type conversions

    • literal value, the go compiler implicitly converts :

      func main() {var myInt int32 =5var myFloat float64 = 6fmt.Print(myInt,"\n")fmt.Print(myFloat)}

      Here is the literal 6 constant integer literals for the integral type: It assigns a value to the float64 type variable, and the compiler makes an implicit type conversion.

    • variables with different underlying types require an explicit type conversion :

func main() {    var myInt int32 =5    //var myFloat float64 = myInt //error    var myFloat float64 = float64(myInt) //需要显式转换    fmt.Print(myInt,"\n")    fmt.Print(myFloat)}

Here is also the distinction between 静态类型 底层类型 :

type IntA int32type IntB int32 func main() {    var a IntA =1    //var b IntB  = a //error    var b IntB  = IntB(a)        fmt.Print(a,"\n")    fmt.Print(b)}

This IntA is the static type of variable A, and the int32 underlying type of variable a. even if the underlying types of the two types are the same, it is necessary to enforce the type conversions when assigning values to each other.

Type conversions for interface type variables, in two cases:

    1. Conversion of a normal type to an interface type: implicitly
    2. Conversion of interface type to normal type: Requires type assertion

All types, according to the official go document, implement an empty interface interface{} , so the normal type can be converted to interface{} type:

func main() {    var x interface{} = "hello" // 字符串常量->interface{}    var y interface{}  = []byte{'w','o','r','l','d'} //[]byte ->interface{}    fmt.Print(x," ")    fmt.Printf("%s",y)}

The conversion of an interface type to a normal type is required Comma-ok断言 or switch测试 to be done.

Comma-ok Assertion

Syntax: Value,ok: = element. T

element must be of type Ingerface, assertion failed, OK is false, otherwise true, the following is a routine:

func main() {    var vars []interface{} = make([]interface{},5)    vars[0] = "one"    vars[1] = "two"    vars[2] = "three"    vars[3] = 10    vars[4] = []byte{'a', 'b', 'c'}    for index, element := range vars {        if value, ok := element.(int); ok {            fmt.Printf("vars[%d] type is int,value is %d \n",index,value)        }else if value,ok := element.(string);ok{            fmt.Printf("vars[%d] type is string,value is %s \n",index,value)        }else if value,ok := element.([]byte);ok{            fmt.Printf("vars[%d] type is []byte,value is %s \n",index,value)        }    

Comma-ok assertions can also be used in this way:

Value: = element. T

However, once an assertion failure results in a run-time error, it is not recommended.

Switch test

The switch test can only be used in a switch statement. Change the above routine to the switch test:

func main() {    var vars []interface{} = make([]interface{}, 5)    vars[0] = "one"    vars[1] = "two"    vars[2] = "three"    vars[3] = 10    vars[4] = []byte{'a', 'b', 'c'}    for index, element := range vars {        switch value := element.(type) {        case int:            fmt.Printf("vars[%d] type is int,value is %d \n", index, value)        case string:            fmt.Printf("vars[%d] type is string,value is %s \n", index, value)        case []byte:            fmt.Printf("vars[%d] type is []byte,value is %s \n", index, value)        }    }}

Finish

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.