題注:阿里巴巴南京研發中心成立一年來業務與團隊迅速擴充套件,誠求前端/後端等各路英才。歡迎關注 [阿里南京技術專刊] (

https://

zhuanlan。zhihu。com/ali-

nanjing)

,也歡迎投遞簡歷,傳送簡歷到 zixiong。zzx@alibaba-inc。com, 誠邀各路大佬前來指教。

Go CheatSheet 是對於 Go 學習/實踐過程中的語法與技巧進行盤點,其屬於 Awesome CheatSheet 系列,致力於提升學習速度與研發效能,即可以將其當做速查手冊,也可以作為輕量級的入門學習資料。 本文參考了許多優秀的文章與程式碼示範,統一宣告在了 Go Links;如果希望深入瞭解某方面的內容,可以繼續閱讀 Go 開發:語法基礎與工程實踐,或者前往 coding-snippets/go 檢視使用 Go 解決常見的資料結構與演算法、設計模式、業務功能方面的程式碼實現。

環境配置與語法基礎

可以前往這裡下載 Go SDK 安裝包,或者使用 brew 等包管理器安裝。go 命令依賴於 $GOPATH 環境變數進行程式碼組織,多專案情況下也可以使用 ln 進行目錄對映以方便進行專案管理。GOPATH 允許設定多個目錄,每個目錄都會包含三個子目錄:src 用於存放原始碼,pkg 用於存放編譯後生成的檔案,bin 用於存放編譯後生成的可執行檔案。

環境配置完畢後,可以使用 go get 獲取依賴,go run 執行程式,go build 來編譯專案生成與包名(資料夾名)一致的可執行檔案。Golang 1。8 之後支援 dep 依賴管理工具,對於空的專案使用 dep init 初始化依賴配置,其會生成

Gopkg。toml Gopkg。lock vendor/

這三個檔案(夾)。

我們可以使用

dep ensure -add github。com/pkg/errors

新增依賴,執行之後,其會在 toml 檔案中新增如下鎖:

[[constraint]]

name = “github。com/pkg/errors”

version = “0。8。0”

簡單的 Go 中 Hello World 程式碼如下:

package

main

import

“fmt”

func

main

()

{

fmt

Println

“hello world”

}

也可以使用 Beego 實現簡單的 HTTP 伺服器:

package

main

import

“github。com/astaxie/beego”

func

main

()

{

beego

Run

()

}

Go 並沒有相對路徑引入,而是以資料夾為單位定義模組,譬如我們新建名為 math 的資料夾,然後使用

package math

來宣告該檔案中函式所屬的模組。

import

mongo

“mywebapp/libs/mongodb/db”

//

對引入的模組重新命名

_

“mywebapp/libs/mysql/db”

//

使用空白下劃線表示僅呼叫其初始化函式

外部引用該模組是需要使用工作區間或者 vendor 相對目錄,其目錄索引情況如下:

cannot find package “sub/math” in any of:

${PROJECTROOT}/vendor/sub/math (vendor tree)

/usr/local/Cellar/go/1。10/libexec/src/sub/math (from $GOROOT)

${GOPATH}/src/sub/math (from $GOPATH)

Go 規定每個原始檔的首部需要進行包宣告,可執行檔案預設放在 main 包中;而各個包中預設首字母大寫的函式作為其他包可見的匯出函式,而小寫函式則預設外部不可見的私有函式。

表示式與控制流

變數宣告與賦值

作為強型別靜態語言,Go 允許我們在變數之後標識資料型別,也為我們提供了自動型別推導的功能。

// 宣告三個變數,皆為 bool 型別

var

c

python

java

bool

// 宣告不同型別的變數,並且賦值

var

i

bool

j

int

=

true

2

// 複雜變數宣告

var

ToBe

bool

=

false

MaxInt

uint64

=

1

<<

64

-

1

z

complex128

=

cmplx

Sqrt

-

5

+

12i

// 短宣告變數

c

python

java

:=

true

false

“no!”

// 宣告常量

const

constant

=

“This is a constant”

在 Go 中,如果我們需要比較兩個複雜物件的相似性,可以使用 reflect。DeepEqual 方法:

m1

:=

map

string

int

{

“a”

1

“b”

2

}

m2

:=

map

string

int

{

“a”

1

“b”

2

}

fmt

Println

reflect

DeepEqual

m1

m2

))

條件判斷

Go 提供了增強型的 if 語句進行條件判斷:

// 基礎形式

if

x

>

0

{

return

x

}

else

{

return

-

x

}

// 條件判斷之前新增自定義語句

if

a

:=

b

+

c

a

<

42

{

return

a

}

else

{

return

a

-

42

}

// 常用的型別判斷

var

val

interface

{}

val

=

“foo”

if

str

ok

:=

val

。(

string

);

ok

{

fmt

Println

str

}

Go 也支援使用 Switch 語句:

// 基礎格式

switch

operatingSystem

{

case

“darwin”

fmt

Println

“Mac OS Hipster”

// 預設 break,不需要顯式宣告

case

“linux”

fmt

Println

“Linux Geek”

default

// Windows, BSD, 。。。

fmt

Println

“Other”

}

// 類似於 if,可以在條件之前新增自定義語句

switch

os

:=

runtime

GOOS

os

{

case

“darwin”

。。。

}

// 使用 switch 語句進行型別判斷:

switch

v

:=

anything

。(

type

{

case

string

fmt

Println

v

case

int32

int64

。。。

default

fmt

Println

“unknown”

}

Switch 中也支援進行比較:

number

:=

42

switch

{

case

number

<

42

fmt

Println

“Smaller”

case

number

==

42

fmt

Println

“Equal”

case

number

>

42

fmt

Println

“Greater”

}

或者進行多條件匹配:

var

char

byte

=

‘?’

switch

char

{

case

‘ ’

‘?’

‘&’

‘=’

‘#’

‘+’

‘%’

fmt

Println

“Should escape”

}

迴圈

Go 支援使用 for 語句進行迴圈,不存在 while 或者 until:

for

i

:=

1

i

<

10

i

++

{

}

// while - loop

for

i

<

10

{

}

// 單條件情況下可以忽略分號

for

i

<

10

{

}

// ~ while (true)

for

{

}

我們也可以使用 range 函式,對於 Arrays 與 Slices 進行遍歷:

// loop over an array/a slice

for

i

e

:=

range

a

{

// i 表示下標,e 表示元素

}

// 僅需要元素

for

_

e

:=

range

a

{

// e is the element

}

// 或者僅需要下標

for

i

:=

range

a

{

}

// 定時執行

for

range

time

Tick

time

Second

{

// do it once a sec

}

Function: 函式

定義,引數與返回值

// 簡單函式定義

func

functionName

()

{}

// 含參函式定義

func

functionName

param1

string

param2

int

{}

// 多個相同型別引數的函式定義

func

functionName

param1

param2

int

{}

// 函式表示式定義

add

:=

func

a

b

int

int

{

return

a

+

b

}

Go 支援函式的最後一個引數使用 。。。 設定為不定引數,即可以傳入一個或多個引數值:

func

adder

args

。。。

int

int

{

total

:=

0

for

_

v

:=

range

args

{

// Iterates over the arguments whatever the number。

total

+=

v

}

return

total

}

adder

1

2

3

// 6

adder

9

9

// 18

nums

:=

[]

int

{

10

20

30

}

adder

nums

。。。

// 60

我們也可以使用 Function Stub 作為函式引數傳入,以實現回撥函式的功能:

func

Filter

s

[]

int

fn

func

int

bool

[]

int

{

var

p

[]

int

// == nil

for

_

v

:=

range

s

{

if

fn

v

{

p

=

append

p

v

}

}

return

p

}

雖然 Go 不是函式式語言,但是也可以用其實現柯里函式(Currying Function):

func

add

x

y

int

int

{

return

x

+

y

}

func

adder

x

int

func

int

int

{

return

func

y

int

int

{

return

add

x

y

}

}

func

main

()

{

add3

:=

adder

3

fmt

Println

add3

4

))

// 7

}

Go 支援多個返回值:

// 返回單個值

func

functionName

()

int

{

return

42

}

// 返回多個值

func

returnMulti

()

int

string

{

return

42

“foobar”

}

var

x

str

=

returnMulti

()

// 命名返回多個值

func

returnMulti2

()

n

int

s

string

{

n

=

42

s

=

“foobar”

// n and s will be returned

return

}

var

x

str

=

returnMulti2

()

閉包: Closure

Go 同樣支援詞法作用域與變數保留,因此我們可以使用閉包來訪問函式定義處外層的變數:

func

scope

()

func

()

int

{

outer_var

:=

2

foo

:=

func

()

int

{

return

outer_var

}

return

foo

}

閉包中並不能夠直接修改外層變數,而是會自動重定義新的變數值:

func

outer

()

func

()

int

int

{

outer_var

:=

2

inner

:=

func

()

int

{

outer_var

+=

99

return

outer_var

// => 101 (but outer_var is a newly redefined

}

return

inner

outer_var

// => 101, 2 (outer_var is still 2, not mutated by inner!)

}

函式執行

Go 中提供了 defer 關鍵字,允許將某個語句的執行推遲到函式返回語句之前:

func

read

。。。

。。。

{

f

err

:=

os

Open

file

。。。

defer

f

Close

()

。。。

return

。。

// f will be closed

異常處理

Go 語言中並不存在 try-catch 等異常處理的關鍵字,對於那些可能返回異常的函式,只需要在函式返回值中新增額外的 Error 型別的返回值:

type

error

interface

{

Error

()

string

}

某個可能返回異常的函式呼叫方式如下:

import

“fmt”

“errors”

func

main

()

{

result

err

:=

Divide

2

0

if

err

!=

nil

{

fmt

Println

err

}

else

{

fmt

Println

result

}

}

func

Divide

value1

int

value2

int

)(

int

error

{

if

value2

==

0

){

return

0

errors

New

“value2 mustn‘t be zero”

}

return

value1

/

value2

nil

}

Go 還為我們提供了 panic 函式,所謂 panic,即是未獲得預期結果,常用於丟擲異常結果。譬如當我們獲得了某個函式返回的異常,卻不知道如何處理或者不需要處理時,可以直接透過 panic 函式中斷當前執行,打印出錯誤資訊、Goroutine 追蹤資訊,並且返回非零的狀態碼:

_

err

:=

os

Create

“/tmp/file”

if

err

!=

nil

{

panic

err

}

資料型別與結構

型別繫結與初始化

Go 中的 type 關鍵字能夠對某個型別進行重新命名:

// IntSlice 並不等價於 []int,但是可以利用型別轉換進行轉換

type

IntSlice

[]

int

a

:=

IntSlice

{

1

2

}

可以使用 T(v) 或者 obj。(T) 進行型別轉換,obj。(T) 僅針對 interface{} 型別起作用:

t

:=

obj

。(

T

// if obj is not T, error

t

ok

:=

obj

。(

T

// if obj is not T, ok = false

// 型別轉換與判斷

str

ok

:=

val

。(

string

);

基本資料型別

interface

{}

// ~ java Object

bool

// true/false

string

int8

int16

int32

int64

int

// =int32 on 32-bit, =int64 if 64-bit OS

uint8

uint16

uint32

uint64

uintptr

uint

byte

// alias for uint8

rune

// alias for int32, represents a Unicode code point

float32

float64

字串

// 多行字串宣告

hellomsg

:=

`

“Hello” in Chinese is 你好 (’Ni Hao‘)

“Hello” in Hindi is नमस्ते (’Namaste‘)

`

格式化字串:

fmt

Println

“Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ”

// basic print, plus newline

p

:=

struct

{

X

Y

int

}{

17

2

}

fmt

Println

“My point:”

p

“x coord=”

p

X

// print structs, ints, etc

s

:=

fmt

Sprintln

“My point:”

p

“x coord=”

p

X

// print to string variable

fmt

Printf

“%d hex:%x bin:%b fp:%f sci:%e”

17

17

17

17。0

17。0

// c-ish format

s2

:=

fmt

Sprintf

“%d %f”

17

17。0

// formatted print to string variable

序列型別

Array 與 Slice 都可以用來表示序列資料,二者也有著一定的關聯。

Array

其中 Array 用於表示固定長度的,相同型別的序列物件,可以使用如下形式建立:

N

Type

N

Type

{

value1

value2

。。。

valueN

}

// 由編譯器自動計算數目

。。。

Type

{

value1

value2

。。。

valueN

}

其具體使用方式為:

// 陣列宣告

var

a

10

int

// 賦值

a

3

=

42

// 讀取

i

:=

a

3

// 宣告與初始化

var

a

=

2

int

{

1

2

}

a

:=

2

int

{

1

2

}

a

:=

。。。

int

{

1

2

}

Go 內建了 len 與 cap 函式,用於獲取陣列的尺寸與容量:

var

arr

=

3

int

{

1

2

3

}

arr

:=

。。。

int

{

1

2

3

}

len

arr

// 3

cap

arr

// 3

不同於 C/C++ 中的指標(Pointer)或者 Java 中的物件引用(Object Reference),Go 中的 Array 只是值(Value)。這也就意味著,當進行陣列複製,或者函式呼叫中的引數傳值時,會複製所有的元素副本,而非僅僅傳遞指標或者引用。顯而易見,這種複製的代價會較為昂貴。

Slice

Slice 為我們提供了更為靈活且輕量級地序列型別操作,可以使用如下方式建立 Slice:

// 使用內建函式建立

make

([]

Type

length

capacity

make

([]

Type

length

// 宣告為不定長度陣列

[]

Type

{}

[]

Type

{

value1

value2

。。。

valueN

}

// 對現有陣列進行切片轉換

array

[:]

array

[:

2

array

2

:]

array

2

3

不同於 Array,Slice 可以看做更為靈活的引用型別(Reference Type),它並不真實地存放陣列值,而是包含陣列指標(ptr),len,cap 三個屬性的結構體。換言之,Slice 可以看做對於陣列中某個段的描述,包含了指向陣列的指標,段長度,以及段的最大潛在長度,其結構如下圖所示:

Go 語法速覽與實踐清單(V0.5)

// 建立 len 為 5,cap 為 5 的 Slice

s

:=

make

([]

byte

5

// 對 Slice 進行二次切片,此時 len 為 2,cap 為 3

s

=

s

2

4

// 恢復 Slice 的長度

s

=

s

[:

cap

s

)]

需要注意的是, 切片操作並不會真實地複製 Slice 中值,只是會建立新的指向原陣列的指標,這就保證了切片操作和運算元組下標有著相同的高效率。不過如果我們修改 Slice 中的值,那麼其會 真實修改底層陣列中的值,也就會體現到原有的陣列中:

d

:=

[]

byte

{

’r‘

’o‘

’a‘

’d‘

}

e

:=

d

2

:]

// e == []byte{’a‘, ’d‘}

e

1

=

’m‘

// e == []byte{’a‘, ’m‘}

// d == []byte{’r‘, ’o‘, ’a‘, ’m‘}

Go 提供了內建的 append 函式,來動態為 Slice 新增資料,該函式會返回新的切片物件,包含了原始的 Slice 中值以及新增的值。如果原有的 Slice 的容量不足以存放新增的序列,那麼會自動分配新的記憶體:

// len=0 cap=0 []

var

s

[]

int

// len=1 cap=2 [0]

s

=

append

s

0

// len=2 cap=2 [0 1]

s

=

append

s

1

// len=5 cap=8 [0 1 2 3 4]

s

=

append

s

2

3

4

// 使用 。。。 來自動展開陣列

a

:=

[]

string

{

“John”

“Paul”

}

b

:=

[]

string

{

“George”

“Ringo”

“Pete”

}

a

=

append

a

b

。。。

// equivalent to “append(a, b[0], b[1], b[2])”

// a == []string{“John”, “Paul”, “George”, “Ringo”, “Pete”}

我們也可以使用內建的 copy 函式,進行 Slice 的複製,該函式支援對於不同長度的 Slice 進行復制,其會自動使用最小的元素數目。同時,copy 函式還能夠自動處理使用了相同的底層陣列之間的 Slice 複製,以避免額外的空間浪費。

func

copy

dst

src

[]

T

int

// 申請較大的空間容量

t

:=

make

([]

byte

len

s

),

cap

s

+

1

*

2

copy

t

s

s

=

t

對映型別

var

m

map

string

int

m

=

make

map

string

int

m

“key”

=

42

// 刪除某個鍵

delete

m

“key”

// 測試該鍵對應的值是否存在

elem

has_value

:=

m

“key”

// map literal

var

m

=

map

string

Vertex

{

“Bell Labs”

{

40。68433

-

74。39967

},

“Google”

{

37。42202

-

122。08408

},

}

Struct & Interface: 結構體與介面

Struct: 結構體

Go 語言中並不存在類的概念,只有結構體,結構體可以看做屬性的集合,同時可以為其定義方法。

// 宣告結構體

type

Vertex

struct

{

// 結構體的屬性,同樣遵循大寫匯出,小寫私有的原則

X

Y

int

z

bool

}

// 也可以宣告隱式結構體

point

:=

struct

{

X

Y

int

}{

1

2

}

// 建立結構體例項

var

v

=

Vertex

{

1

2

}

// 讀取或者設定屬性

v

X

=

4

// 顯示宣告鍵

var

v

=

Vertex

{

X

1

Y

2

}

// 宣告陣列

var

v

=

[]

Vertex

{{

1

2

},{

5

2

},{

5

5

}}

方法的宣告也非常簡潔,只需要在 func 關鍵字與函式名之間宣告結構體指標即可,該結構體會在不同的方法間進行復制:

func

v

Vertex

Abs

()

float64

{

return

math

Sqrt

v

X

*

v

X

+

v

Y

*

v

Y

}

// Call method

v

Abs

()

對於那些需要修改當前結構體物件的方法,則需要傳入指標:

func

v

*

Vertex

add

n

float64

{

v

X

+=

n

v

Y

+=

n

}

var

p

*

Person

=

new

Person

// pointer of type Person

Pointer: 指標

// p 是 Vertex 型別

p

:=

Vertex

{

1

2

}

// q 是指向 Vertex 的指標

q

:=

&

p

// r 同樣是指向 Vertex 物件的指標

r

:=

&

Vertex

{

1

2

}

// 指向 Vertex 結構體物件的指標型別為 *Vertex

var

s

*

Vertex

=

new

Vertex

Interface: 介面

Go 允許我們透過定義介面的方式來實現多型性:

// 介面宣告

type

Awesomizer

interface

{

Awesomize

()

string

}

// 結構體並不需要顯式實現介面

type

Foo

struct

{}

// 而是透過實現所有介面規定的方法的方式,來實現介面

func

foo

Foo

Awesomize

()

string

{

return

“Awesome!”

}

type

Shape

interface

{

area

()

float64

}

func

getArea

shape

Shape

float64

{

return

shape

area

()

}

type

Circle

struct

{

x

y

radius

float64

}

type

Rectangle

struct

{

width

height

float64

}

func

circle

Circle

area

()

float64

{

return

math

Pi

*

circle

radius

*

circle

radius

}

func

rect

Rectangle

area

()

float64

{

return

rect

width

*

rect

height

}

func

main

()

{

circle

:=

Circle

{

x

0

y

0

radius

5

}

rectangle

:=

Rectangle

{

width

10

height

5

}

fmt

Printf

“Circle area: %f\n”

getArea

circle

))

fmt

Printf

“Rectangle area: %f\n”

getArea

rectangle

))

}

//Circle area: 78。539816

//Rectangle area: 50。000000

慣用的思路是先定義介面,再定義實現,最後定義使用的方法:

package

animals

type

Animal

interface

{

Speaks

()

string

}

// implementation of Animal

type

Dog

struct

{}

func

a

Dog

Speaks

()

string

{

return

“woof”

}

/** 在需要的地方直接引用 **/

package

circus

import

“animals”

func

Perform

a

animal

Animal

{

return

a

Speaks

()

}

Go 也為我們提供了另一種介面的實現方案,我們可以不在具體的實現處定義介面,而是在需要用到該介面的地方,該模式為:

func

funcName

a

INTERFACETYPE

CONCRETETYPE

定義介面:

package

animals

type

Dog

struct

{}

func

a

Dog

Speaks

()

string

{

return

“woof”

}

/** 在需要使用實現的地方定義介面 **/

package

circus

type

Speaker

interface

{

Speaks

()

string

}

func

Perform

a

Speaker

{

return

a

Speaks

()

}

Embedding

Go 語言中並沒有子類繼承這樣的概念,而是透過嵌入(Embedding)的方式來實現類或者介面的組合。

// ReadWriter 的實現需要同時滿足 Reader 與 Writer

type

ReadWriter

interface

{

Reader

Writer

}

// Server 暴露了所有 Logger 結構體的方法

type

Server

struct

{

Host

string

Port

int

*

log

Logger

}

// 初始化方式並未受影響

server

:=

&

Server

{

“localhost”

80

log

New

。。。

)}

// 卻可以直接呼叫內嵌結構體的方法,等價於 server。Logger。Log(。。。)

server

Log

。。。

// 內嵌結構體的名詞即是型別名

var

logger

*

log

Logger

=

server

Logger

併發程式設計

Goroutines

Goroutines 是輕量級的執行緒,可以參考併發程式設計導論一文中的程序、執行緒與協程的討論;Go 為我們提供了非常便捷的 Goroutines 語法:

// 普通函式

func

doStuff

s

string

{

}

func

main

()

{

// 使用命名函式建立 Goroutine

go

doStuff

“foobar”

// 使用匿名內部函式建立 Goroutine

go

func

x

int

{

// function body goes here

}(

42

}

Channels

通道(Channel)是帶有型別的管道,可以用於在不同的 Goroutine 之間傳遞訊息,其基礎操作如下:

// 建立型別為 int 的通道

ch

:=

make

chan

int

// 向通道中傳送值

ch

<-

42

// 從通道中獲取值

v

:=

<-

ch

// 讀取,並且判斷其是否關閉

v

ok

:=

<-

ch

// 讀取通道,直至其關閉

for

i

:=

range

ch

{

fmt

Println

i

}

譬如我們可以在主執行緒中等待來自 Goroutine 的訊息,並且輸出:

// 建立通道

messages

:=

make

chan

string

// 執行 Goroutine

go

func

()

{

messages

<-

“ping”

}()

// 阻塞,並且等待訊息

msg

:=

<-

messages

// 使用通道進行併發地計算,並且阻塞等待結果

c

:=

make

chan

int

go

sum

s

[:

len

s

/

2

],

c

go

sum

s

len

s

/

2

:],

c

x

y

:=

<-

c

<-

c

// 從 c 中接收

如上建立的是無緩衝型通道(Non-buffered Channels),其是阻塞型通道;當沒有值時讀取方會持續阻塞,而寫入方則是在無讀取時阻塞。我們可以建立緩衝型通道(Buffered Channel),其讀取方在通道被寫滿前都不會被阻塞:

ch

:=

make

chan

int

100

// 傳送方也可以主動關閉通道

close

ch

Channel 同樣可以作為函式引數,並且我們可以顯式宣告其是用於傳送資訊還是接收資訊,從而增加程式的型別安全度:

// ping 函式用於傳送資訊

func

ping

pings

chan

<-

string

msg

string

{

pings

<-

msg

}

// pong 函式用於從某個通道中接收資訊,然後傳送到另一個通道中

func

pong

pings

<-

chan

string

pongs

chan

<-

string

{

msg

:=

<-

pings

pongs

<-

msg

}

func

main

()

{

pings

:=

make

chan

string

1

pongs

:=

make

chan

string

1

ping

pings

“passed message”

pong

pings

pongs

fmt

Println

<-

pongs

}

同步

同步,是併發程式設計中的常見需求,這裡我們可以使用 Channel 的阻塞特性來實現 Goroutine 之間的同步:

func

worker

done

chan

bool

{

time

Sleep

time

Second

done

<-

true

}

func

main

()

{

done

:=

make

chan

bool

1

go

worker

done

// 阻塞直到接收到訊息

<-

done

}

Go 還為我們提供了 select 關鍵字,用於等待多個通道的執行結果:

// 建立兩個通道

c1

:=

make

chan

string

c2

:=

make

chan

string

// 每個通道會以不同時延輸出不同值

go

func

()

{

time

Sleep

1

*

time

Second

c1

<-

“one”

}()

go

func

()

{

time

Sleep

2

*

time

Second

c2

<-

“two”

}()

// 使用 select 來同時等待兩個通道的執行結果

for

i

:=

0

i

<

2

i

++

{

select

{

case

msg1

:=

<-

c1

fmt

Println

“received”

msg1

case

msg2

:=

<-

c2

fmt

Println

“received”

msg2

}

}

Web 程式設計

HTTP Server

package

main

import

“fmt”

“net/http”

// define a type for the response

type

Hello

struct

{}

// let that type implement the ServeHTTP method (defined in interface http。Handler)

func

h

Hello

ServeHTTP

w

http

ResponseWriter

r

*

http

Request

{

fmt

Fprint

w

“Hello!”

}

func

main

()

{

var

h

Hello

http

ListenAndServe

“localhost:4000”

h

}

// Here’s the method signature of http。ServeHTTP:

// type Handler interface {

// ServeHTTP(w http。ResponseWriter, r *http。Request)

// }

Beego

利用 Beego 官方推薦的 bee 命令列工具,我們可以快速建立 Beego 專案,其目錄組織方式如下:

quickstart

├──

conf

└──

app

conf

├──

controllers

└──

default

go

├──

main

go

├──

models

├──

routers

└──

router

go

├──

static

├──

css

├──

img

└──

js

├──

tests

└──

default_test

go

└──

views

└──

index

tpl

在 main。go 檔案中,我們可以啟動 Beego 例項,並且呼叫路由的初始化配置檔案:

package

main

import

_

“quickstart/routers”

“github。com/astaxie/beego”

func

main

()

{

beego

Run

()

}

而在路由的初始化函式中,我們會宣告各個路由與控制器之間的對映關係:

package

routers

import

“quickstart/controllers”

“github。com/astaxie/beego”

func

init

()

{

beego

Router

“/”

&

controllers

MainController

{})

}

也可以手動指定 Beego 專案中的靜態資源對映:

beego

SetStaticPath

“/down1”

“download1”

beego

SetStaticPath

“/down2”

“download2”

在具體的控制器中,可以設定返回資料,或者關聯的模板名:

package

controllers

import

“github。com/astaxie/beego”

type

MainController

struct

{

beego

Controller

}

func

this

*

MainController

Get

()

{

this

Data

“Website”

=

“beego。me”

this

Data

“Email”

=

“astaxie@gmail。com”

this

TplNames

=

“index。tpl”

// version 1。6 use this。TplName = “index。tpl”

}

DevPractics: 開發實踐

檔案讀寫

import

“io/ioutil”

。。。

datFile1

errFile1

:=

ioutil

ReadFile

“file1”

if

errFile1

!=

nil

{

panic

errFile1

}

。。。

測試

VSCode 可以為函式自動生成基礎測試用例,並且提供了方便的用例執行與除錯的功能。

/** 交換函式 */

func

swap

x

*

int

y

*

int

{

x

y

=

y

x

}

/** 自動生成的測試函式 */

func

Test_swap

t

*

testing

T

{

type

args

struct

{

x

*

int

y

*

int

}

tests

:=

[]

struct

{

name

string

args

args

}{

// TODO: Add test cases。

}

for

_

tt

:=

range

tests

{

t

Run

tt

name

func

t

*

testing

T

{

swap

tt

args

x

tt

args

y

})

}

}