---
# System prepended metadata

title: '[Golang] 初見Golang - 基礎篇'
tags: [Golang]

---

# [Golang] 初見Golang - 基礎篇

###### tags: `Golang`

:::info
:bookmark: 筆者有寫過C、C++、C#的經驗，以下內容是與這三個語言進行的比較。**目標是為讓已經*有過其他語言經驗*的人更快上手**
:::

## 1. `{}`的宗教戰爭
想必各位看過在程式開發者發生過的「宗教戰爭」，`TAB`派 vs. `SPACE`派；大括號`{}`寫在函式的同一行？還是下一行。雖然聽起來很無聊，但程式設計師們真的會因為這是吵架，但是`Golang`是一門狠毒的語言，他連讓我們吵的機會都沒有，剝奪了我們的權利。

==`Golang`的大括號**必須**寫在與函式同一行，不然強行報錯==

```go=
// 價值正確
func() {

}

// ...價值不夠，需儲值
func()
{

}
```

## 2. 「我的話已經說完了」
開頭有說過，最常寫的是`C`語言家族，在語句結束後需要加`;`表示結束，但`Golang`不需要。

PS 這不表示`Golang`沒有`;`，在下面第六點`for`迴圈時會講到

## 3. 入口在哪裡

```go=
package main

func main() {

}
```

`function main`並不陌生，但它一定得在`package main`裡面，如果你像筆者一樣是由`C`語言家族來的，會認為它只有一個入口，編譯下去會把所有文件編譯完之後進到`main`，然後程式就動了起來，但`Golang`不一樣的地方在於，你應把它視為一個資料夾唯一個模塊(module)，像`Python`那樣。

PS 一個資料夾為一個模塊，`Golang`並沒有硬性規定，但建議這麼做，會讓專案乾淨很多。

## 4. `using namespace`
第三點提到`package main`中的`main`，可以把它想像成它就是在`C++`和`C#`中的`namspace`

如果要引用別的模塊：

```go=
package main

import "fmt"

func main() {
    fmt.Println("Hello, Golang!")
}
```

假若是要引用多個module呢?

```go=
package main

import (
    "fmt"
    "time"
)


// time，表示time模塊裡的Now函式
// 如果轉換成C++類似: time::Now()

func main() {
    fmt.Println("Hello, Golang! " + time.Now())
}
```

## 5. 變數宣告

變數宣告有三種：
```cpp=
var name type = val // #1
var name = val      // #2
name := val         // #3
```
這裡的`var`是`Golang`的關鍵字，`type`是指類型，不是內建的關鍵字，怎麼使用`type`關鍵字會在下面講到

第一種是合法的，但是有點多此一舉，通常會是這樣用：
```go=
// 宣告一個變數，但不初始化他
var name type

//如果你要需很多變數
var (
    name1 type1
    name2 type2
    name3 type3
)
```
如果宣告一堆變數，但型別都相同
```go=
// 我覺得可讀性不佳
var name1, name2, name3 type
```
第二種和第三種都是開發者宣告變數，然後進行賦值，判斷型別就給編譯器處理。


**Q:** `=`跟`:=`的差別
**A:**`=`是賦值，`:=`是宣告變數並賦值 [[原文點這裡]](https://segmentfault.com/q/1010000007160096)

## 6. 常量與enum

### 常量(const)
```go=
// 跟變數宣告一樣，但宣告後就必須賦值
const (
 	Monday, Tuesday, Wednesday = 1, 2, 3
 	Thursday, Friday, Saturday = 4, 5, 6
)
```

對於enum，嗯?!**`Golang`沒有提供enum**，可以用常量代替enum

## 7. Unused variable
在`C`、`C++`、`C#`中，如果有沒有使用的變數，編譯器會給出warning(當然你也可以把級別往上調)，但在`Golang`中，編譯器查到有沒有用的變數會直接報錯，對!它就是這麼摳，一個多的變數都不准出現。

## 8. `if` `for` `while` `switch`流程控制
```go=
// if...else...
im := "handsome"

if you := im; you == "handsome" {
    fmt.Println("You say you are " + you + ", 不要臉")
} else if you == "ugly" {
    fmt.Println("You say you are " + you + ", 真誠實")
} else {
    fmt.Println("你甚麼都不是")
}

// for
for i := 0; i < 10; i++ {
    fmt.Println("I'm genius.")
} 

// switch
today := Friday

switch today {
case Monday:
case Tuesday:
case Wednesday:
case Thursday:
case Friday:
	fmt.Println("Work, Work, Work")
case Saturday:
case Sunday:
	fmt.Println("Play, play, play")
default:
	fmt.Println("You are alien, right?")
}
```

發現到`if`、`for`、`switch`的條件句沒有小括號包起來。

等一下!那`while`呢?還有最常用到的`while(true)`無限loop呢?
`Golang`沒有提供`while`關鍵字，至於無限loop，請見：
```go=
for {
    // 我想要出去！FREE ME!!!
    break
}
```

當然`Golang`也有`break`和`continue`關鍵字

## 8. 函式的模樣
長這樣 :point_down: 
```go=
func MyFunctionName(para type) type {
    type name
    // process data
    return name
}
```

## 10. 可以回傳「不只一個」值
在C語言家族中，如果你想回傳多個值會把你要的類型再包成一個結構，或是用`tuple`，但在`Goalng`你可以直接回傳N個值：

```go=
func MyFunctionName(para type) (type1, typ2) {
    type1 name1
    type2 name2
    // process data
    return name1, name2
}
```

調用函式長這樣 :point_down: 
```go=
a1, b1 := MyFunction(some_para)
```

## 11. 你回傳的，我不要
根據上述的一點，如果定義出來的變數沒有使用，會是error，於是`Golang`
使用底線(`_`)代表不使用的回傳值，一樣我們使用上面的例子：

```go=
a1, _ := MyFunction(some_para)

// 你不可以這樣
＿, _ := MyFunction(some_para)
```

## 12. 資料包起來
這樣寫 :point_down: 
```go=
type Vec2 struct {
	x float64
	y float64
}

type Entity struct {
	name string
	pos  Vec2
}

// ... 宣告並定義結構

point := Vec2{
	x: 32.0,
	y: 45.0,
}

poka := Enity {
    name: "POKA",
    pos:  point,
}

```

還有一個功能較匿名結構，我們假設一個匿名結構有一個`int`型別的`id`和一個`Enity`

```go=
poka := struct {
    id uint32
    e  Entity
}{
    id: 23,
    e: Entity{
        name: "POKA",
        pos:  point,
    },    
}
```


## 13. 他有指標!
這聽起來很恐怖，但是比起在`C`和`C++`中作的奇淫邪技，`Golang`的指標簡單了不少。

第一`Golang`有GC的機制所以不是開發者主動釋放記憶體，第二指標不能做運算。

與`C`和`C++`一樣有分為一般指標和動態配置
```go=
// 一般指標
n := 100
nPtr := &n

// 動態配置
nPtr := new(int)
*nPtr = 100
```

通常動態配置對複合型別(自定義的結構)

```go=
type User struct {
    id    int
    name  string
}

// ...

user := new(User)
```
除了`new`以外，還有一個`make`函式也是用在動態配置上： [詳情文章](https://medium.com/d-d-mag/golang-%E7%AD%86%E8%A8%98-make-%E8%88%87-new-%E7%9A%84%E5%B7%AE%E5%88%A5-68b05c7ce016)

### ~~Pass by value/reference~~ ==ERROR==
~~當然有指標函式的參數就會有傳值(pass by value)和傳址(pass by  reference)~~
```go=
func ChangeNameByVal(u User, n string) {
    u.name = n
}

func ChangeNameByRef(u *User, n string) {
    u.name = n
}
func main() {

    user := User{
        id:   999,
        name: "POKA",
    }
    
    // 不會變
    ChangeNameByVal(user, "Charlie")
    fmt.Println(user.name)
    
    // 會變
    ChangeNameByRef(&user, "Charlie")
    fmt.Println(user.name)
}
```

:::warning
:warning: 
雖說Golang有指標，但是並們有所謂的「Pass By Reference」，跟C一樣只有「Pass By Value」，事實上仔細從函式傳遞參數的方式就略知一二，跟C的方法完全一模一樣。

> 2020.11.09
:::


## 14. 在函式結束的時候
`defer`寫在函式調用前面表示該還是在`return`**之後**才會被執行
```go=
func main() {
    defer fmt.Println("World")
    fmt.Println("Hello")
}

// Output: 
//     Hello
//     World   
```

[這裡](https://stackoverflow.com/questions/52718143/is-golang-defer-statement-execute-before-or-after-return-statement)有更詳細的範例及回答 

通常這會用在資源釋放的時候，例如：讀取IO文件、切斷連線、互斥鎖(Mutex)


## 參考資料
- [使用指標 (Pointer)](https://michaelchen.tech/golang-programming/pointer/#%E5%BB%BA%E7%AB%8B%E6%8C%87%E6%A8%99)
- [Pass by pointer vs pass by value in Go](https://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value/)
- [defer、panic、recover](https://openhome.cc/Gossip/Go/DeferPanicRecover.html)
- [golang 匿名 struct 的使用方式](https://javasgl.github.io/go-anonymous-struct/)