// type comes after variable name.
funcadd(xint,yint)int{returnx+y;}// consecutive named function parameters share a type
funcadd(x,yint)int{returnx+y;}// function can return any number of results
funcswap(x,ystring)(string,string){returny,x}// naked return: a return without arguments, can harm readability in longer
// functions
funcsplit(sumint)(x,yint){x=sum*4/9y=sum-xreturn}
Methods
Go has no classes, but you can define methods on types. A method is a function with a special receiver argument.
packagemainimport("fmt""time")funcmain(){t:=time.Now()// same as: switch true
switch{// default will always be last evaluated
default:fmt.Println("default")caset.Hour()<12:fmt.Println("morning")caset.Hour()<17,true:// like useing '||', match any
fmt.Println("afternoon")fallthroughcasefalse:// even condition is false, fallthrough do fallthrough here
fmt.Println("Are you ok?")// cannot put fallthrough in the last case or default
// fallthrough
}}
Defer
a defer statement defers the execution of a function until the surrounding function returns.
arguments evaluated immediately, but function call is not executed until the surrounding function returns.
defered function calls are pushed onto a stack, so executed in last-in-first-outo order
struct fields can also be accessed through a struct pointer, without explicit dereferencing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
packagemainimport"fmt"funcmain(){typeVertexstruct{XintYint}v:=Vertex{1,2}p:=&v// pointer to a struct
v.X=4(*p).Y=6// dereference the struct first - cumbersum
p.Y=5// without explicit dereference
fmt.Println(v)}
struct literal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
packagemainimport"fmt"typeVertexstruct{X,Yint}funcmain(){var(v1=Vertex{1,2}// {1, 2}, has type Vertex
v2=Vertex{Y:1}// {0, 1}
v3=Vertex{}// {0, 0}
p=&Vertex{3,4}// has type *Vertex
)// {1 2} {0 1} {0 0} &{3 4}
fmt.Println(v1,v2,v3,p)}
if the backing array is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.
range
1
2
3
4
5
6
7
8
9
10
11
12
13
14
packagemainimport"fmt"varpow=[]int{1,2,4,8,16,32,64,128}funcmain(){// for i, _ := range pow
// for i := range pow
// for _, v := range pow
fori,v:=rangepow{fmt.Printf("2**%d = %d\n",i,v)}}
packagemainimport"fmt"typeVertexstruct{X,Yint}funcmain(){varv=map[int]Vertex{3:{1,2},}// x := v[2]
x,ok:=v[2]// ok is false
if!ok{fmt.Println("no v[2]")fmt.Printf("x is the zero value of Vertex, which is %v\n",x)}fmt.Println(v[3])// insert or update an element
v[3]=Vertex{3,4}// delete a key
delete(v,3)}
Function Type
Functions are values too, they can be used as funtion arguments and return values.
packagemainimport("fmt""math")typeVertexstruct{X,Yfloat64}func(vVertex)Abs()float64{returnmath.Sqrt(v.X*v.X+v.Y*v.Y)}func(v*Vertex)Scale(ffloat64){v.X=v.X*fv.Y=v.Y*f}funcf(fnfunc(Vertex)float64,v*Vertex){fmt.Println(fn(*v))}funcf2(fnfunc(*Vertex)float64,v*Vertex){fmt.Println(fn(v))}funcmain(){v:=Vertex{3,4}f(Vertex.Abs,&v)f2((*Vertex).Abs,&v)// these two are different function
// receiver is actually the first argument of method
fmt.Printf("%T\n",(*Vertex).Scale)fmt.Printf("%T\n",v.Scale)}
A closure is a function value that references variables from outside its body.
packagemainimport("fmt""math")typeAbserinterface{Abs()float64}funcmain(){varaAbserf:=MyFloat(-math.Sqrt2)v:=Vertex{3,4}a=f// a MyFloat implements Abser
a=&v// a *Vertex implements Abser
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
// a = v
fmt.Println(a.Abs())}typeMyFloatfloat64func(fMyFloat)Abs()float64{iff<0{returnfloat64(-f)}returnfloat64(f)}typeVertexstruct{X,Yfloat64}func(v*Vertex)Abs()float64{returnmath.Sqrt(v.X*v.X+v.Y*v.Y)}
printing value and type of an interface is the same as printing its underlying value and type.
packagemainimport"fmt"typeVertexstruct{x,yint}// value of type *Vertex can also call method M()
func(Vertex)M(){}funcmain(){varvVertexvariinterface{M()}=v// argument here must implement methods of the interface
t,ok:=i.(*Vertex)des(t)// *main.Vertex, <nil>
chk(t,ok)t2,ok:=i.(Vertex)des(t2)// main.Vertex, {0, 0}
chk(t2,ok)}funcdes(vinterface{}){fmt.Printf("%T, %v\n",v,v)}funcchk(tinterface{},okbool){if!ok{fmt.Println("type not correct, zero value returned:",t)}else{fmt.Println("type correct, value is:",t)}}
packagemainimport"fmt"funcdo(iinterface{}){switchv:=i.(type){caseint:fmt.Printf("Twice %v is %v\n",v,v*2)casestring:fmt.Printf("%q is %v bytes long\n",v,len(v))default:// here v has the same type as i
fmt.Printf("I don't know about type %T!\n",v)}}funcmain(){do(21)do("hello")do(true)}
Zero values
variables declared without an explicit initial value are given their zero value.
0 for numeric types
false for the boolean type
"" (the empty string) for the strings
{<default field values>} for structs
nil for slice (len and cap of a nil slice is 0)
nil for maps
nil for interfaces
nil for pointers
Common Interfaces
Error
When fmt prints values, it looks for the error interface first:
1
2
3
typeerrorinterface{Error()string}
if the interface value is not <nil>, the Error() method will be invoked by fmt to get the error string.
Do not print the interface value in the Error() method directly, it will cause infinite loop.
1
2
3
4
5
6
7
typeErrNegativeSqrtfloat64func(eErrNegativeSqrt)Error()string{// do not print e directly, infinite loop here
// fmt.Println(e)
returnfmt.Sprintln("cannot Sqrt negative number: ",float64(e))}
Reader
the io.Reader interface has a Read method:
1
func(T)Read(b[]byte)(nint,errerror)
read populates the given byte slice with data and returns the number of bytes populated and an error value.
a goroutine is a lightweight thread managed by the Go runtime.
Channels
By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
Channels aren’t like files, you don’t usually need to close them. Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.
packagemainimport"fmt"funcfib(nint,cchanint){a,b:=0,1fori:=0;i<n;i++{c<-aa,b=b,a+b}close(c)}funcmain(){c:=make(chanint)gofib(10,c)forx:=rangec{fmt.Println(x)}// "ok" is false if there are:
// 1. no more values to receive
// 2. and the channel is closed
// x, ok := <- c
}
Select
select statement lets a goroutine wait on multiple communication operations.
select blocks until one of its cases can run (by adding a default case, it won’t block). It chooses one at random if multiple are ready.
// SafeCounter is safe to use concurrently.
typeSafeCounterstruct{musync.Mutexvmap[string]int}func(c*SafeCounter)Value(keystring)int{c.mu.Lock()// Lock so only one goroutine at a time can access the map c.v.
deferc.mu.Unlock()returnc.v[key]}
packagemainimport"fmt"// fibonacci is a function that returns
// a function that returns an int.
funcfibonacci()func()int{a,b:=0,1returnfunc()int{ret:=aa,b=b,a+breturnret}}funcmain(){f:=fibonacci()fori:=0;i<10;i++{fmt.Println(f())}}
packagemainimport("fmt""math")typeErrNegativeSqrtfloat64func(eErrNegativeSqrt)Error()string{// float64(e) here is important
// fmt.Sprint(e) will cause infinite loop!
returnfmt.Sprint("cannot Sqrt negative number:",float64(e))}funcSqrt(xfloat64)(float64,error){ifx<0{returnx,ErrNegativeSqrt(x)}returnmath.Sqrt(x),nil}funcmain(){fmt.Println(Sqrt(2))fmt.Println(Sqrt(-2))}
packagemainimport("io""os""strings")typerot13Readerstruct{rio.Reader}func(rot13readerrot13Reader)Read(b[]byte)(int,error){n,err:=rot13reader.r.Read(b)iferr!=nil{return0,io.EOF}fori:=0;i<n;i++{switchc:=b[i];{casec>='A'&&c<='Z':b[i]='A'+(b[i]-'A'+13)%26casec>='a'&&c<='z':b[i]='a'+(b[i]-'a'+13)%26}}returnn,nil}funcmain(){s:=strings.NewReader("Lbh penpxrq gur pbqr!")r:=rot13Reader{s}io.Copy(os.Stdout,&r)}
packagemainimport("fmt""sync")typeFetcherinterface{// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(urlstring)(bodystring,urls[]string,errerror)}typeurl2Depstruct{musync.Mutexmpmap[string]int}func(u*url2Dep)insertUrl(urlstring,depint){u.mu.Lock()deferu.mu.Unlock()u.mp[url]=dep}func(u*url2Dep)getDep(urlstring)(int,bool){u.mu.Lock()deferu.mu.Unlock()dep,ok:=u.mp[url]returndep,ok}// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
funcCrawl(u*url2Dep,urlstring,depthint,fetcherFetcher){deferwg.Done()ifdepth<=0{return}ifdep,ok:=u.getDep(url);!ok||dep<depth{u.insertUrl(url,depth)}else{fmt.Printf("visited: %s\n",url)return}body,urls,err:=fetcher.Fetch(url)iferr!=nil{fmt.Println(err)return}fmt.Printf("found: %s %q\n",url,body)for_,nextUrl:=rangeurls{wg.Add(1)goCrawl(u,nextUrl,depth-1,fetcher)}}varwgsync.WaitGroupfuncmain(){u:=url2Dep{mp:make(map[string]int)}wg.Add(1)goCrawl(&u,"https://golang.org/",4,fetcher)wg.Wait()}// fakeFetcher is Fetcher that returns canned results.
typefakeFetchermap[string]*fakeResulttypefakeResultstruct{bodystringurls[]string}func(ffakeFetcher)Fetch(urlstring)(string,[]string,error){ifres,ok:=f[url];ok{returnres.body,res.urls,nil}return"",nil,fmt.Errorf("not found: %s",url)}// fetcher is a populated fakeFetcher.
varfetcher=fakeFetcher{"https://golang.org/":&fakeResult{"The Go Programming Language",[]string{"https://golang.org/pkg/","https://golang.org/cmd/",},},"https://golang.org/pkg/":&fakeResult{"Packages",[]string{"https://golang.org/","https://golang.org/cmd/","https://golang.org/pkg/fmt/","https://golang.org/pkg/os/",},},"https://golang.org/pkg/fmt/":&fakeResult{"Package fmt",[]string{"https://golang.org/","https://golang.org/pkg/",},},"https://golang.org/pkg/os/":&fakeResult{"Package os",[]string{"https://golang.org/","https://golang.org/pkg/",},},}