---
# System prepended metadata

title: import導包及init調用

---

# import導包及init調用

## GOPATH & GOROOT

- GOPATH：Go的Workspace，集中管理code、package和執行檔。GOPATH可以設定為自己想要的位置，以後Go專案需要的依賴庫都會存放在這個路徑下面。
- GOROOT：為Go SDK的安裝目錄，官方的程式庫所在位置就是在GOROOT裡面。

Windows系統下可藉由 `go env` 指令查詢到Go相關的環境變數設定，其中包括**GOPATH**和**GOROOT**的路徑：

```go=
..
set GOPATH= C:\Users\userName\go
set GOROOT= C:\Go
..
```

## Package
同一個 package 的所有 Go 檔案會放在同一個資料夾內；而這個資料夾內的所有檔案也都會屬於同一個 package，有相同的 package 名稱。

package分為兩類：

- 可執行包(executable package)：可自己執行，表示有package main
- 工具包(utility package)：不可自己執行，但是可以給可執行包做擴展應用

一般來說我們可以在 `$GOPATH/src` 下創立專案資料夾，然後在專案資料夾再去新增 package 資料夾。

:::warning
import package 時Go會先在 $GOROOT/src 下尋找指定的package。
若找不到就往 $GOPATH/src 目錄下尋找，再找不到就會報出編譯錯誤。
:::

這樣架構看起來就會是：

```go=
C:\Users\userName\go\src\golangStudy //專案資料夾
	┏━ example1 //package example1
	┃━ example2 //package example2
	┃━ .
	┃━ .
	┖━ main.go
```

example1 , example2 package內容為：

```go=
package example1 //通常package名稱會與資料夾名稱相同

import "fmt"

func Ex1() {
	fmt.Println("example1")
}
```

```go=
package example2

import "fmt"

func Ex2() {
	fmt.Println("example2")
}
```

### import方法

當我今天 在別的package需要import package 以及 使用package中的函式 時有幾種方法：

- `"packagename"`
- `. "packagename"`：將example1內的所有方法都引入，就不用使用 `example1.funcname` 去呼叫 (不建議)
- `name "packagename"`：轉換為別名，和Python 寫 `import packagename as name` 同樣意思
- `_ "packagename"`：僅導入該package的 init函式 (在往下會提到)

```go=
package main

import (
  "fmt"
  . "golangStudy/example1" //將example1內的所有方法都引入 , 就不用使用example1.funcname 去呼叫 (不建議)
  exam2 "golangStudy/example2" //轉換為別名 , 和Python 的 import some as other 同樣意思
)

func main(){
  fmt.Println("Hello World")
  example1.Ex1() //使用example1 package中的Ex1 func , 輸出為 example1
  example2.Ex2() //使用example2 package中的Ex2 func , 輸出為 example2
}
```

### 外部引用

這邊提一下為什麼 Ex1 這個函式名稱開頭要使用大寫英文字母

在 Go 語言中，沒辦法明確標明 public、private、protected，而是以變數名稱的第一個字母大小寫來判斷能否被外部引用。當我今天要調用example1包內的函式，但是由於該函式名稱首字母為小寫，因此會報錯：

```go=
ex1 not exported by package example1
```

所以希望函式能夠被外部引用請將函式名稱的第一個字母寫為大寫哦！

### package的命名

Go崇尚取名簡潔明瞭，好的package名稱只使用小寫字母，如：

- time
- list
- http

其他語言典型的命名方式並不適用於Go，如：

- 小駝峰：computerServiceClient
- 底線連結：priority_queue

可以明智的使用縮寫，當程序員熟悉縮寫名稱時即可使用，廣泛使用的有：

- strconv (字串轉換)
- syscall (系統調用)
- fmt (格式化的I/O)

## Modules

在Go 1.11 1.12發布時新的包管理機制[Go Modules](https://blog.golang.org/using-go-modules)出現了，可以不再需要 GOPATH 的存在。Go Modules的管理系統使依賴包版本訊息明確而且也比較容易管理。

Go Modules的優點：

- **自動下載依賴包**
- 項目不必放在 `GOPATH/src` 內了
- 項目內會生成一个 `go.mod`，列出依賴的包
- 所有第三方包會顯示版本號
- 對於已經轉移的包可以用`replace`聲明替換，不需要改代碼

### GO111MODULE
我們需要添加環境變數 `GO111MODULE` 而值有三種可以選擇：

1. auto：自動模式，項目若是在 `$GOPATH/src` 裡會使用 `$GOPATH/src` 的依賴包，在`$GOPATH/src` 外，就使用 `go.mod` 裡 **require** 的包。
2. on：開啟模式，1.12後，無論在 `$GOPATH/src` 裡還是在外面，都會使用 `go.mod` 裡 **require** 的包
3. off：關閉模式。

默認情況下，`GO111MODULE` = `auto`

![](https://i.imgur.com/mev8uH2.png)

### 初始化Module
首先 cd 進入你的go專案資料夾內(ex: C:\Users\userName\go\src\golangStudy)

輸入指令`go mod init modulename` 以初始化模組，modulename為模組名稱

```go=
C:\Users\userName\go\src\golangStudy > go mod init modulename //宣告module path
go: creating new go.mod: module main    
go: to add module requirements and sums:
        go mod tidy
```

接著專案資料夾內會產生 `go.mod`

```go=
module golangstudy //模塊名稱
go 1.12 //go版本號
```

直接 `go run mian.go` ，Go會自動查找代碼中的包去下載，並且把具體的依賴關係和版本寫入到 `go.mod` 和 `go.sum` 中。

```go=
module golangstudy
go 1.12
require(
  packagename v1.0.0
  //.
  //.
 )
```

require 代表引用，後面是包名，最後v1.0.0 是引用的版本號。
:::info
使用Go Modules的包管理方式，依賴的第三方包被下載到了 `$GOPATH/pkg/mod` 路徑下，所以你可以在這個路徑找到你剛剛下載的包。同一個包還可以保存不同版本，只需要在go.mod中指定版本即可，若在go.mod中沒有指定版本，Go命令則會自動下載程式碼中使用到的依賴包的最新版本。
:::

## init函式
Go語言中同一個package底下不可以有重複的變數或者是函式名稱，唯獨init函式可以在同一個package內宣告多次，並且依序執行下來。

init的執行順序會比main還要先，無論init函式寫在哪個位置都一樣。

```go=
package main

import (
    "fmt"
)

func init() {
    fmt.Println("init 1")
}

func init() {
    fmt.Println("init 2")
}

func main() {
    fmt.Println("Hello World")
}
```

### 從其他 package 讀取 init 函式
在package前面加上 `_` 為非導入模式，即僅讓該包執行初始化函式：

```go=
import(
    _ "example1"
)
```

:::warning
注：只要 package 內有 init 函式，在引入 package 時都會被執行。
:::

假設我今天在example1和example2兩個package中都加上init函式：

```go=
package example1 //通常package名稱會與資料夾名稱相同

import "fmt"

func init(){
	fmt.Println("example1 init")
}

func Ex1() {
	fmt.Println("example1")
}

```

```go=
package example2

import "fmt"

func init(){
	fmt.Println("example2 init")
}

func Ex2() {
	fmt.Println("example2")
}
```

再由 main 去調用

```go=
package main

import (
  	"fmt"
	"golangStudy/example1" //引入時執行該包init函式
	"golangStudy/example2" //引入時執行該包init函式
)

func main() {
	fmt.Println("hello main")
	example1.Ex1()
	example2.Ex2()
}
```

```go=
example1 init
example2 init
hello main
example1
example2
```

