Go is a simple and interesting language, but like other languages, it has some skills... Most of these techniques are not due to go's flaws. If you used to use other languages, some of these mistakes are natural pitfalls. Others are caused by false assumptions and lack of detail.
If you take the time to learn the language, read official notes, wiki s, mailing list discussions, lots of great blogs, Rob Pike's presentations, and source code, most of these tips are obvious. Although not everyone starts learning in this way, it doesn't matter. If you are new to Go, the information here will save you a lot of time debugging your code.
catalog
-
Primary section
-
Open braces cannot be placed on a single line
-
Unused variables
-
Unused Imports
-
A simple variable declaration can only be used inside a function
-
Repeat variable declaration with simple declaration
-
Accidental variable hiding
-
Variable cannot be initialized with 'nil' without explicit type
-
Using "nil" Slices and Maps
-
Map capacity
-
String will not be 'nil'
-
Parameters of Array function
-
Unexpected value when Slice and Array use the "range" statement
-
Slices and Arrays are one-dimensional
-
Access to nonexistent Map Keys
-
Strings cannot be modified
-
Conversion between String and Byte Slice
-
String and index operations
-
String is not always UTF8 text
-
Length of string
-
Missing commas in multiline Slice, Array, and Map statements
-
log.Fatal and log.Panic are not just logs
-
Built in data structure operation is not synchronous
-
Iterative value of String in "range" statement
-
Use "for range" statement iteration for Map
-
Invalidation behavior in "switch" Declaration
-
Auto increase and auto decrease
-
Bitwise NOT operation
-
Differences in operational priorities
-
Structures not exported will not be encoded
-
Application exit under active Goroutines
-
Send a message to a non cached Channel and return as soon as the target receiver is ready
-
Sending to a closed Channel causes Panic
-
Using "nil" Channels
-
The receiver of the value passing method cannot modify the original value
-
Advanced
-
Turn off HTTP response
-
Close HTTP connection
-
Compare structures, arrays, slices, and maps
-
Recover from Panic
-
Update the value of reference element in Slice, Array, and Map "range" statement
-
Hide data in Slice
-
Slice's data "destroyed"
-
"Tasteless" Slices
-
Type declaration and method
-
Jump out of "for switch" and "for select" code blocks
-
Iterative variables and closures in the "for" Declaration
-
Evaluation of parameter of Defer function call
-
Executed by function call of Defer
-
Failed type assertion
-
Blocked Goroutine and resource leakage
-
Advanced
-
Use pointer to receive an instance of the value of the method
-
Update Map values
-
Values for 'nil' interfaces and 'nil' interfaces
-
Stack and heap variables
-
GOMAXPROCS, concurrent, and parallel
-
Reordering of read and write operations
-
Priority scheduling
Primary section
Open braces cannot be placed on a single line
- level: beginner
In most other languages that use braces, you need to choose where to place them. The way to Go is different. You can thank for the automatic semicolon injection (no pre reading). Yes, there are semicolons in Go: -)
Examples of failures:
package main import"fmt" func main(){//error, can't have the opening brace on a separate line fmt.Println("hello there!")}
Compilation error:
/tmp/sandbox826898458/main.go:6: syntax error: unexpected semicolon or newline before {
Valid examples:
package main import"fmt" func main(){ fmt.Println("works!")}
Unused variables
- level: beginner
If you have unused variables, the code fails to compile. There are exceptions, of course. You must use declared variables within a function, but unused global variables are OK.
If you assign a new value to an unused variable, the code still fails to compile. You need to use this variable somewhere in order for the compiler to compile happily.
Fails:
package main var gvar int//not an error func main(){var one int//error, unused variable two :=2//error, unused variablevar three int//error, even though it's assigned 3 on the next line three =3}
Compile Errors:
/tmp/sandbox473116179/main.go:6: one declared andnot used /tmp/sandbox473116179/main.go:7: two declared andnot used /tmp/sandbox473116179/main.go:8: three declared andnot used
Works:
package main import"fmt" func main(){var one int _ = one two :=2 fmt.Println(two)var three int three =3 one = three var four int four = four }
Another option is to comment out or remove unused variables: -)
Unused Imports
- level: beginner
If you introduce a package without using any of its functions, interfaces, structures, or variables, the code will fail to compile.
If you really need to introduce a package, you can add an underscore flag, _, as the name of the package to avoid compilation failure. The glide line marker is used for import, but not for use.
Fails:
package main import("fmt""log""time") func main(){}
Compile Errors:
/tmp/sandbox627475386/main.go:4: imported andnot used:"fmt"/tmp/sandbox627475386/main.go:5: imported andnot used:"log"/tmp/sandbox627475386/main.go:6: imported andnot used:"time"
Works:
package main import( _ "fmt""log""time")var _ = log.Println func main(){ _ = time.Now}
Another option is to remove or comment out unused imports: -)
A simple variable declaration can only be used inside a function
- level: beginner
Fails:
package main myvar :=1//error func main(){}
Compile Error:
/tmp/sandbox265716165/main.go:3: non-declaration statement outside function body
Works:
package main var myvar =1 func main(){}
Repeat variable declaration with simple declaration
- level: beginner
You cannot declare a variable repeatedly in a single declaration, but this is allowed in a multivariable declaration, where at least one new declaration variable is required.
Duplicate variables need to be in the same code block, otherwise you will get a hidden variable.
Fails:
package main func main(){ one :=0 one :=1//error}
Compile Error:
/tmp/sandbox706333626/main.go:5:nonew variables on left side of :=
Works:
package main func main(){ one :=0 one, two :=1,2 one,two = two,one }
Accidental variable hiding
- level: beginner
The syntax of short variable declaration is so convenient (especially for developers who have used dynamic languages), it is easy to regard it as a normal allocation operation. If you make this error in a new code block, there will be no compilation errors, but your application will not do what you expect.
package main import"fmt" func main(){ x :=1 fmt.Println(x)//prints 1{ fmt.Println(x)//prints 1 x :=2 fmt.Println(x)//prints 2} fmt.Println(x)//prints 1 (bad if you need 2)}
Even for experienced Go developers, this is a very common pitfall. The pit is easy to dig, but hard to find.
Variable cannot be initialized with 'nil' without explicit type
- level: beginner
The "nil" flag is used to represent the "zero value" of interface, function, maps, slices, and channels. If you don't specify the type of the variable, the compiler won't be able to compile your code because it can't guess the specific type.
Fails:
package main func main(){var x =nil//error _ = x }
Compile Error:
/tmp/sandbox188239583/main.go:4:use of untyped nil
Works:
package main func main(){var x interface{}=nil _ = x }
Using "nil" Slices and Maps
- level: beginner
Adding elements to a "nil" slice is fine, but doing the same for a map will generate a runtime panic.
Works:
package main func main(){var s []int s = append(s,1)}
Fails:
package main func main(){var m map[string]int m["one"]=1//error}
Map capacity
- level: beginner
You can specify the capacity of the map when it is created, but you cannot use the cap() function on the map.
Fails:
package main func main(){ m := make(map[string]int,99) cap(m)//error}
Compile Error:
/tmp/sandbox326543983/main.go:5: invalid argument m (type map[string]int)for cap
String will not be 'nil'
- level: beginner
This is an important point for developers who often use "nil" to assign string variables.
Fails:
package main func main(){var x string=nil//errorif x ==nil{//error x ="default"}}
Compile Errors:
/tmp/sandbox630560459/main.go:4: cannot usenilas type stringin assignment /tmp/sandbox630560459/main.go:6: invalid operation: x ==nil(mismatched types stringandnil)
Works:
package main func main(){var x string//defaults to "" (zero value)if x ==""{ x ="default"}}
Parameters of Array function
-level: beginner
If you are a C or C + + developer, arrays are pointers to you. When you pass arrays to a function, the function references the same memory area so that they can modify the original data. An array in Go is a number, so when you pass an array to a function, the function gets a copy of the original array data. This will be a problem if you plan to update the array data.
package main import"fmt" func main(){ x :=[3]int{1,2,3} func(arr [3]int){ arr[0]=7 fmt.Println(arr)//prints [7 2 3]}(x) fmt.Println(x)//prints [1 2 3] (not ok if you need [7 2 3])}
If you need to update the data of the original array, you can use the array pointer type.
package main import"fmt" func main(){ x :=[3]int{1,2,3} func(arr *[3]int){(*arr)[0]=7 fmt.Println(arr)//prints &[7 2 3]}(&x) fmt.Println(x)//prints [7 2 3]}
Another option is to use slice. Even if your function gets a copy of slice, it still references the original data.
package main import"fmt" func main(){ x :=[]int{1,2,3} func(arr []int){ arr[0]=7 fmt.Println(arr)//prints [7 2 3]}(x) fmt.Println(x)//prints [7 2 3]}
Unexpected value when Slice and Array use the "range" statement
- level: beginner
This happens if you use "for in" or "foreach" statements in other languages. The syntax of "range" in Go is different. It gets two values: the first is the index of the element, and the other is the data of the element.
Bad:
package main import"fmt" func main(){ x :=[]string{"a","b","c"}for v := range x { fmt.Println(v)//prints 0, 1, 2}}
Good:
package main import"fmt" func main(){ x :=[]string{"a","b","c"}for _, v := range x { fmt.Println(v)//prints a, b, c}}
Slices and Arrays are one-dimensional
- level: beginner
It seems that Go supports multidimensional Array and Slice, but it is not. Although you can create arrays of arrays or slices of slices. For the application of numerical calculation which depends on dynamic multidimensional Array, Go is far away in performance and complexity.
You can build a dynamic multidimensional array by using a pure dimensional array, a slice of an independent slice, and a slice of a shared data slice.
If you use a one-dimensional array, you need to deal with indexes, boundary checks, and memory reallocation when the array needs to be larger.
Using "independent" slice to create a dynamic multidimensional array takes two steps. First, you need to create an external slice. Then, you need to assign each internal slice. The internal slice is independent of each other. You can increase and decrease them without affecting other internal slices.
package main func main(){ x :=2 y :=4 table := make([][]int,x)for i:= range table { table[i]= make([]int,y)}}
Using slice of shared data slice to create a dynamic multidimensional array takes three steps. First, you need to create a data "container" to hold the raw data. Then you create the external slice. Finally, each internal slice is initialized by re slicing the original data slice.
package main import"fmt" func main(){ h, w :=2,4 raw := make([]int,h*w)for i := range raw { raw[i]= i } fmt.Println(raw,&raw[4])//prints: [0 1 2 3 4 5 6 7] <ptr_addr_x> table := make([][]int,h)for i:= range table { table[i]= raw[i*w:i*w + w]} fmt.Println(table,&table[1][0])//prints: [[0 1 2 3] [4 5 6 7]] <ptr_addr_x>}
There have been special applications for multidimensional array and slice, but now it looks like this is a low priority feature.
Access to nonexistent Map Keys
-level: beginner
This is a trick for developers who want to get the "nil" designator (as in other languages). If the zero value of the corresponding data type is nil, the returned value will be nil, but it is different for other data types. Detecting the corresponding "zero value" can be used to determine whether the records in the map exist, but this is not always credible (for example, if the "zero value" in the binary map is false, then what do you do). The most reliable way to detect the existence of records in a given map is to check the second returned value through the access operation of the map.
Bad:
package main import"fmt" func main(){ x := map[string]string{"one":"a","two":"","three":"c"}if v := x["two"]; v ==""{//incorrect fmt.Println("no entry")}}
Good:
package main import"fmt" func main(){ x := map[string]string{"one":"a","two":"","three":"c"}if _,ok := x["two"];!ok { fmt.Println("no entry")}}
Strings cannot be modified
- level: beginner
An attempt to update a single character in a string variable using an index operation will fail. String is a read-only byte slice (and some extra properties). If you do need to update a string, use byte slice and convert it to string type if necessary.
Fails:
package main import"fmt" func main(){ x :="text" x[0]='T' fmt.Println(x)}
Compile Error:
/tmp/sandbox305565531/main.go:7: cannot assign to x[0]
Works:
package main import"fmt" func main(){ x :="text" xbytes :=[]byte(x) xbytes[0]='T' fmt.Println(string(xbytes))//prints Text}
Note that this is not the right way to update characters in a text string, because a given character may be stored in more than one byte. If you do need to update a text string, first convert it to a run slice. Even with run slice, a single character can occupy multiple rune s, such as when your character has a specific accent. This complex and fuzzy "character" nature is the reason why Go strings are represented by byte sequences.
Conversion between String and Byte Slice
- level: beginner
When you convert a string to a byte slice (or vice versa), you get a full copy of the original data. This is different from the cast operation in other languages, as well as the re slice operation when the new slice variable points to the same array used by the original byte slice.
Go does use some optimizations in the [] byte to string and string to [] byte transformations to avoid additional allocations (more optimizations in the todo list).
The first optimization avoids the extra allocation when [] bytekey is used to query in the map[string] collection: m[string(key)].
The second optimization avoids the extra allocation of: for I, V: = range [] byte (STR) {...} in the for range statement after the string is converted to [] byte.
String and index operations
- level: beginner
An index operation on a string returns a byte value instead of a character (as in other languages).
package main import"fmt" func main(){ x :="text" fmt.Println(x[0])//print 116 fmt.Printf("%T",x[0])//prints uint8}
If you need access to a specific string of "characters" (unicode encoded points / runs), use for range. The official "unicode/utf8" package and the experimental utf8string package (golang.org/x/exp/utf8string) can also be used. The utf8string package contains a convenient At() method. Also an option is to convert strings to run slices.
String is not always UTF8 text
- level: beginner
The value of the string does not need to be the text of UTF8. They can contain any byte. The string will only be UTF8 if it is used by string literal. Even then they can use escape sequences to contain other data.
To know if the string is UTF8, you can use the ValidString() function in the "unicode/utf8" package.
package main import("fmt""unicode/utf8") func main(){ data1 :="ABC" fmt.Println(utf8.ValidString(data1))//prints: true data2 :="A\xfeC" fmt.Println(utf8.ValidString(data2))//prints: false}
Length of string
- level: beginner
Let's assume you are a Python developer. You have the following code:
data = u'♥'print(len(data))#prints: 1
You may be surprised when you convert it to Go code.
package main import"fmt" func main(){ data :="♥" fmt.Println(len(data))//prints: 3}
The built-in len() function returns the number of byte s, rather than the number of characters in a calculated unicode string like Python.
To get the same results in Go, use the RuneCountInString() function in the "unicode/utf8" package.
package main import("fmt""unicode/utf8") func main(){ data :="♥" fmt.Println(utf8.RuneCountInString(data))//prints: 1}
In theory, the RuneCountInString() function does not return the number of characters, because a single character may occupy multiple runes.
package main import("fmt""unicode/utf8") func main(){ data :="é" fmt.Println(len(data))//prints: 3 fmt.Println(utf8.RuneCountInString(data))//prints: 2}
Missing commas in multiline Slice, Array, and Map statements
- level: beginner
Fails:
package main func main(){ x :=[]int{1,2//error} _ = x }
Compile Errors:
/tmp/sandbox367520156/main.go:6: syntax error: need trailing comma before newline in composite literal /tmp/sandbox367520156/main.go:8: non-declaration statement outside function body /tmp/sandbox367520156/main.go:9: syntax error: unexpected }
Works:
package main func main(){ x :=[]int{1,2,} x = x y :=[]int{3,4,}//no error y = y }
When you collapse a declaration to a single line, you won't get compilation errors if you don't add a comma at the end.
log.Fatal and log.Panic are not just logs
- level: beginner
Logging libraries generally provide different log levels. Unlike these logging libraries, the log package in Go can do more than log when you call its Fatal * () and Panic * () functions. When your application calls these functions, Go will also terminate the application: -)
package main import"log" func main(){ log.Fatalln("Fatal Level: log entry")//app exits here log.Println("Normal Level: log entry")}
Built in data structure operation is not synchronous
- level: beginner
Even if Go itself has many features to support concurrency, concurrent and secure data set consolidation is not one of them: -) it's your job to ensure that data sets are updated atomically. Goroutines and channels are recommended ways to implement these atomic operations, but you can also use the "sync" package if it makes sense for your application.
Iterative value of String in "range" statement
- level: beginner
The index value (the first value returned by the range operation) is the index of the first byte of the current unicode point / run of the second value returned. It is not the index of the current "character", which is different from other languages. Note that real characters may be represented by multiple rune s. If you need to process characters, make sure you use the "norm" package (golang.org/x/text/unicode/norm).
The for range statement of the string variable will attempt to translate the data into UTF8 text. For any byte sequence it cannot understand, it will return 0xfffd runs (that is, unicode replacement characters), rather than real data. If you save any (non UTF8 text) data in a string variable, make sure to convert them to byte slice to get all the saved data.
package main import"fmt" func main(){ data :="A\xfe\x02\xff\x04"for _,v := range data { fmt.Printf("%#x ",v)}//prints: 0x41 0xfffd 0x2 0xfffd 0x4 (not ok) fmt.Println()for _,v := range []byte(data){ fmt.Printf("%#x ",v)}//prints: 0x41 0xfe 0x2 0xff 0x4 (good)}
Use "for range" statement iteration for Map
- level: beginner
You need this technique if you want to get elements in a certain order (for example, by key value). Each map iteration will produce different results. Go's runtime tries to randomize the iteration sequence, but it doesn't always succeed, so you may get some of the same map iteration results. So don't be surprised if you see five identical iterations in a row.
package main import"fmt" func main(){ m := map[string]int{"one":1,"two":2,"three":3,"four":4}for k,v := range m { fmt.Println(k,v)}}
And if you use Go's playground( https://play.golang.org/ )You'll always get the same result, because it won't recompile code unless you modify it.
Invalidation behavior in "switch" Declaration
- level: beginner
The 'case' block in the 'switch' declaration statement break s by default. This is different from the default behavior of entering the next "next" block in other languages.
package main import"fmt" func main(){ isSpace := func(ch byte)bool{switch(ch){case' '://errorcase'\t':returntrue}returnfalse} fmt.Println(isSpace('\t'))//prints true (ok) fmt.Println(isSpace(' '))//prints false (not ok)}
You can force "case" code blocks to enter by using "fallthrough" at the end of each "case" block. You can also override the switch statement to use the list of expressions in the case block.
package main import"fmt" func main(){ isSpace := func(ch byte)bool{switch(ch){case' ','\t':returntrue}returnfalse} fmt.Println(isSpace('\t'))//prints true (ok) fmt.Println(isSpace(' '))//prints true (ok)}
Auto increase and auto decrease
- level: beginner
Many languages have auto increment and auto decrement operations. Unlike other languages, Go does not support pre version operations. You can't use these two operators in expressions either.
Fails:
package main import"fmt" func main(){ data :=[]int{1,2,3} i :=0++i //error fmt.Println(data[i++])//error}
Compile Errors:
/tmp/sandbox101231828/main.go:8: syntax error: unexpected ++/tmp/sandbox101231828/main.go:9: syntax error: unexpected ++, expecting :
Works:
package main import"fmt" func main(){ data :=[]int{1,2,3} i :=0 i++ fmt.Println(data[i])}
Bitwise NOT operation
- level: beginner
Many languages use ~ as a NOT operator (that is, bitwise complement), but Go reuses the XOR operator (^) for this.
Fails:
package main import"fmt" func main(){ fmt.Println(~2)//error}
Compile Error:
/tmp/sandbox965529189/main.go:6: the bitwise complement operatoris^
Works:
package main import"fmt" func main(){var d uint8 =2 fmt.Printf("%08b\n",^d)}
Go still uses ^ as the XOR operator, which may confuse some people.
If you like, you can use a binary XOR operation (for example, 0x02 XOR 0xff) to represent a unary NOT operation (for example, NOT 0x02). This explains why ^ is reused to represent unary NOT operations.
Go also has a special "AND NOT" bitwise operation (& ^), which also makes NOT operation more confusing. This seems to require a special feature / hack to support A AND (NOT B) without parentheses.
package main import"fmt" func main(){var a uint8 =0x82var b uint8 =0x02 fmt.Printf("%08b [A]\n",a) fmt.Printf("%08b [B]\n",b) fmt.Printf("%08b (NOT B)\n",^b) fmt.Printf("%08b ^ %08b = %08b [B XOR 0xff]\n",b,0xff,b ^0xff) fmt.Printf("%08b ^ %08b = %08b [A XOR B]\n",a,b,a ^ b) fmt.Printf("%08b & %08b = %08b [A AND B]\n",a,b,a & b) fmt.Printf("%08b &^%08b = %08b [A 'AND NOT' B]\n",a,b,a &^ b) fmt.Printf("%08b&(^%08b)= %08b [A AND (NOT B)]\n",a,b,a &(^b))}
Differences in operational priorities
- level: beginner
In addition to the "bit clear" operation (& ^), Go is also a collection of standard operators shared with many other languages. Although the priority of operations is not always the same.
package main import"fmt" func main(){ fmt.Printf("0x2 & 0x2 + 0x4 -> %#x\n",0x2&0x2+0x4)//prints: 0x2 & 0x2 + 0x4 -> 0x6//Go: (0x2 & 0x2) + 0x4//C++: 0x2 & (0x2 + 0x4) -> 0x2 fmt.Printf("0x2 + 0x2 << 0x1 -> %#x\n",0x2+0x2<<0x1)//prints: 0x2 + 0x2 << 0x1 -> 0x6//Go: 0x2 + (0x2 << 0x1)//C++: (0x2 + 0x2) << 0x1 -> 0x8 fmt.Printf("0xf | 0x2 ^ 0x2 -> %#x\n",0xf|0x2^0x2)//prints: 0xf | 0x2 ^ 0x2 -> 0xd//Go: (0xf | 0x2) ^ 0x2//C++: 0xf | (0x2 ^ 0x2) -> 0xf}
Structures not exported will not be encoded
- level: beginner
Structures that start with lowercase letters will not be encoded (json, xml, gob, etc.), so when you encode these non exported structures, you will get a zero value.
Fails:
package main import("fmt""encoding/json") type MyDatastruct{Oneint two string} func main(){in:=MyData{1,"two"} fmt.Printf("%#v\n",in)//prints main.MyData{One:1, two:"two"} encoded,_ := json.Marshal(in) fmt.Println(string(encoded))//prints {"One":1}varoutMyData json.Unmarshal(encoded,&out) fmt.Printf("%#v\n",out)//prints main.MyData{One:1, two:""}}
Application exit under active Goroutines
- level: beginner
The application will not have to be completed with all goroutines. This is a common mistake for beginners. Everyone starts at a certain level, so it's not shameful to make a beginner's mistake: -)
package main import("fmt""time") func main(){ workerCount :=2for i :=0; i < workerCount; i++{ go doit(i)} time.Sleep(1* time.Second) fmt.Println("all done!")} func doit(workerId int){ fmt.Printf("[%v] is running\n",workerId) time.Sleep(3* time.Second) fmt.Printf("[%v] is done\n",workerId)}
You will see:
[0]is running [1]is running all done!
One of the most common solutions is to use the "WaitGroup" variable. It will let the main goroutine wait for all workers to complete. If your application has a worker with a long-running message processing loop, you will also need a way to signal these goroutines to exit. You can send a "kill" message to each worker. Another option is to turn off a channel that all workers receive. This is a simple way to signal all goroutines at once.
package main import("fmt""sync") func main(){var wg sync.WaitGroupdone:= make(chan struct{}) workerCount :=2for i :=0; i < workerCount; i++{ wg.Add(1) go doit(i,done,wg)} close(done) wg.Wait() fmt.Println("all done!")} func doit(workerId int,done<-chan struct{},wg sync.WaitGroup){ fmt.Printf("[%v] is running\n",workerId) defer wg.Done()<-done fmt.Printf("[%v] is done\n",workerId)}
If you run the app, you will see:
[0]is running [0]isdone[1]is running [1]isdone
It seems that all worker s are finished before the main goroutine exits. Great! However, you will also see this:
fatal error: all goroutines are asleep - deadlock!
This is not very good: -) sent Shenma? Why deadlock? Workers quit, and they execute wg.Done(). There should be no problem with the application.
Deadlock occurs because each worker gets a copy of the original "WaitGroup" variable. When the worker executes wg.Done(), it does not take effect on the "WaitGroup" variable on the main goroutine.
package main import("fmt""sync") func main(){var wg sync.WaitGroupdone:= make(chan struct{}) wq := make(chan interface{}) workerCount :=2for i :=0; i < workerCount; i++{ wg.Add(1) go doit(i,wq,done,&wg)}for i :=0; i < workerCount; i++{ wq <- i } close(done) wg.Wait() fmt.Println("all done!")} func doit(workerId int, wq <-chan interface{},done<-chan struct{},wg *sync.WaitGroup){ fmt.Printf("[%v] is running\n",workerId) defer wg.Done()for{select{case m :=<- wq: fmt.Printf("[%v] m => %v\n",workerId,m)case<-done: fmt.Printf("[%v] is done\n",workerId)return}}}
Now it will work as expected: -)
Send a message to a non cached Channel and return as soon as the target receiver is ready
- level: beginner
The sender will not be blocked unless the message is being processed by the receiver. Depending on the machine you are running the code on, the receiver's goroutine may or may not have enough time to process the message before the sender continues execution.
package main import"fmt" func main(){ ch := make(chan string) go func(){for m := range ch { fmt.Println("processed:",m)}}() ch <-"cmd.1" ch <-"cmd.2"//won't be processed}
Sending to a closed Channel causes Panic
- level: beginner
It is safe to receive from a closed channel. The return value of ok in the received state will be set to false, which means no data is received. If you receive from a channel with cache, you will get the cached data first. Once it is empty, the returned ok value will be false.
Sending data to a closed channel causes panic. This behavior is documented, but new Go developers have different intuitions, and they may want the sending behavior to be similar to the receiving behavior.
package main import("fmt""time") func main(){ ch := make(chan int)for i :=0; i <3; i++{ go func(idx int){ ch <-(idx +1)*2}(i)}//get the first result fmt.Println(<-ch) close(ch)//not ok (you still have other senders)//do other work time.Sleep(2* time.Second)}
According to different applications, the repair methods will be different. It may be a small code change, or it may be necessary to change the design of the application. Either way, you need to make sure your app doesn't send data to a closed channel.
The bug example above can be fixed by using a special abandoned channel to signal the remaining worker s that they no longer need their results.
package main import("fmt""time") func main(){ ch := make(chan int)done:= make(chan struct{})for i :=0; i <3; i++{ go func(idx int){select{case ch <-(idx +1)*2: fmt.Println(idx,"sent result")case<-done: fmt.Println(idx,"exiting")}}(i)}//get first result fmt.Println("result:",<-ch) close(done)//do other work time.Sleep(3* time.Second)}
Using "nil" Channels
- level: beginner
Send and receive operations on a nil channel are permanently blocked. This behavior is well documented, but it's a surprise for new Go developers.
package main import("fmt""time") func main(){var ch chan intfor i :=0; i <3; i++{ go func(idx int){ ch <-(idx +1)*2}(i)}//get first result fmt.Println("result:",<-ch)//do other work time.Sleep(2* time.Second)}
If you run the code you will see a runtime error:
fatal error: all goroutines are asleep - deadlock!
This behavior can be used in the select declaration to dynamically turn on and off case code blocks.
package main import"fmt"import"time" func main(){ inch := make(chan int) outch := make(chan int) go func(){varin<- chan int= inch varout chan <-intvar val intfor{select{caseout<- val:out=nilin= inch case val =<-in:out= outch in=nil}}}() go func(){for r := range outch { fmt.Println("result:",r)}}() time.Sleep(0) inch <-1 inch <-2 time.Sleep(3* time.Second)}
The receiver of the value passing method cannot modify the original value
- level: beginner
The receiver of a method is like a regular function parameter. If declared as a value, your function / method gets a copy of the recipient parameter. This means that changes made to the receiver will not affect the original value unless the receiver is a map or slice variable, and you update the elements in the collection, or the receiver of the domain you update is a pointer.
package main import"fmt" type data struct{ num int key *string items map[string]bool} func (this*data) pmethod(){this.num =7} func (this data) vmethod(){this.num =8*this.key ="v.key"this.items["vmethod"]=true} func main(){ key :="key.1" d := data{1,&key,make(map[string]bool)} fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)//prints num=1 key=key.1 items=map[] d.pmethod() fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)//prints num=7 key=key.1 items=map[] d.vmethod() fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)//prints num=7 key=v.key items=map[vmethod:true]}
Original address: levy.at/blog/11