這是 Go 語言從零到 Web 應用系列的第三篇。上一篇我們設定好了開發環境,現在要來學 Go 的基礎語法。
變數宣告
Go 有幾種宣告變數的方式。
var 宣告
// 宣告並指定型別
var name string = "Jack"
// 宣告並讓編譯器推斷型別
var age = 30
// 先宣告,之後再賦值(會有零值)
var score int // score = 0
var active bool // active = false
var label string // label = ""
短變數宣告 :=
// 只能在函式內使用
name := "Jack"
age := 30
pi := 3.14
isReady := true
:= 是最常用的宣告方式,簡潔直覺。但注意它只能在函式內使用,套件層級的變數必須用 var。
多變數宣告
// 同時宣告多個變數
var x, y, z int = 1, 2, 3
// 短變數宣告也可以
a, b, c := "hello", 42, true
// 分組宣告
var (
firstName string = "Jack"
lastName string = "Tse"
age int = 30
)
常數
const Pi = 3.14159
const MaxRetries = 3
// 分組宣告
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
常數在編譯時就確定值,不能被修改。
iota:自動遞增常數
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota 在每個 const 區塊中從 0 開始,每行自動加 1。非常適合定義列舉值。
基本型別
數值型別
// 整數
var i int = 42 // 平台相依(32 或 64 bit)
var i8 int8 = 127 // -128 ~ 127
var i16 int16 = 32767
var i32 int32 = 2147483647
var i64 int64 = 9223372036854775807
// 無符號整數
var u uint = 42
var u8 uint8 = 255 // 0 ~ 255,等同 byte
var u16 uint16 = 65535
var u32 uint32 = 4294967295
var u64 uint64 = 18446744073709551615
// 浮點數
var f32 float32 = 3.14
var f64 float64 = 3.141592653589793 // 預設用 float64
常用原則:整數用 int,浮點數用 float64,除非有特殊需求。
字串
// 字串是不可變的 byte 序列
s := "Hello, 世界"
// 取得長度(byte 數量)
fmt.Println(len(s)) // 13(中文字佔 3 bytes)
// 取得字元數量
fmt.Println(utf8.RuneCountInString(s)) // 9
// 字串串接
greeting := "Hello" + ", " + "World"
// 多行字串(反引號)
json := `{
"name": "Jack",
"age": 30
}`
// 常用字串操作
strings.Contains(s, "Hello") // true
strings.HasPrefix(s, "He") // true
strings.ToUpper("hello") // "HELLO"
strings.Split("a,b,c", ",") // ["a", "b", "c"]
strings.TrimSpace(" hi ") // "hi"
布林值
isReady := true
isDone := false
// 布林運算
result := isReady && isDone // AND
result = isReady || isDone // OR
result = !isReady // NOT
零值(Zero Values)
Go 的每個型別都有零值,宣告但未初始化的變數會自動獲得零值:
| 型別 | 零值 |
|---|---|
| int, float | 0 |
| string | "" |
| bool | false |
| pointer, slice, map, channel, function, interface | nil |
型別轉換
Go 沒有隱式型別轉換,所有轉換必須明確表達:
i := 42
f := float64(i) // int → float64
u := uint(i) // int → uint
f2 := 3.14
i2 := int(f2) // float64 → int(截斷小數,得到 3)
// 字串與數字的轉換
import "strconv"
s := strconv.Itoa(42) // int → string: "42"
n, err := strconv.Atoi("42") // string → int: 42
條件判斷
if / else
score := 85
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else if score >= 70 {
fmt.Println("C")
} else {
fmt.Println("F")
}
Go 的 if 有一個特別的功能——可以在條件前加一個初始化語句:
// err 的作用域僅限於 if/else 區塊內
if err := doSomething(); err != nil {
fmt.Println("Error:", err)
return
}
這個模式在 Go 中非常常見,特別是在錯誤處理時。
switch
day := "Monday"
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("工作日")
case "Saturday", "Sunday":
fmt.Println("週末")
default:
fmt.Println("無效的日期")
}
Go 的 switch 與其他語言不同:
- 不需要 break:每個 case 執行完自動跳出
- 可以匹配多個值:用逗號分隔
- 可以沒有條件:變成 if/else 的替代
// 無條件 switch(更清晰的 if/else chain)
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
default:
fmt.Println("F")
}
迴圈
Go 只有 for 一種迴圈,但它能表達所有迴圈形式。
標準 for
for i := 0; i < 5; i++ {
fmt.Println(i)
}
while 形式
count := 0
for count < 10 {
count++
}
無限迴圈
for {
fmt.Println("永遠執行")
// 用 break 跳出
break
}
range 遍歷
// 遍歷 slice
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Printf("index: %d, value: %d\n", index, value)
}
// 只要 index
for i := range nums {
fmt.Println(i)
}
// 只要 value(用 _ 忽略 index)
for _, v := range nums {
fmt.Println(v)
}
// 遍歷字串(得到 rune)
for i, ch := range "Hello, 世界" {
fmt.Printf("index: %d, char: %c\n", i, ch)
}
// 遍歷 map
m := map[string]int{"a": 1, "b": 2}
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
break 與 continue
for i := 0; i < 10; i++ {
if i == 3 {
continue // 跳過這次迭代
}
if i == 7 {
break // 跳出迴圈
}
fmt.Println(i)
}
// 輸出:0 1 2 4 5 6
函式
基本函式
func greet(name string) string {
return "Hello, " + name + "!"
}
// 呼叫
message := greet("Jack")
多值回傳
Go 的函式可以回傳多個值,這是它最有特色的功能之一:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}
// 呼叫
result, err := divide(10, 3)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result)
多值回傳最常見的用法就是回傳 (結果, error),這是 Go 錯誤處理的核心模式。
命名回傳值
func rectangleArea(width, height float64) (area float64) {
area = width * height
return // 自動回傳命名的變數
}
命名回傳值會在函式開始時自動初始化為零值。適合用在回傳值含義需要明確表達的場景。
可變參數
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// 呼叫
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
// 展開 slice
nums := []int{1, 2, 3}
fmt.Println(sum(nums...)) // 6
函式作為值
Go 中的函式是一等公民(first-class citizen),可以當作值傳遞:
// 將函式賦值給變數
add := func(a, b int) int {
return a + b
}
fmt.Println(add(3, 4)) // 7
// 函式作為參數
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, v := range nums {
result[i] = fn(v)
}
return result
}
doubled := apply([]int{1, 2, 3}, func(n int) int {
return n * 2
})
// doubled = [2, 4, 6]
指標
指標存放的是記憶體位址。Go 有指標但沒有指標運算(比 C 更安全)。
基本用法
x := 42
p := &x // p 是指向 x 的指標,型別是 *int
fmt.Println(p) // 印出記憶體位址,如 0xc0000b4008
fmt.Println(*p) // 解參考,印出 42
*p = 100 // 透過指標修改值
fmt.Println(x) // 100
為什麼需要指標?
// 不用指標:函式內的修改不影響外部
func doubleValue(n int) {
n = n * 2 // 只修改了複本
}
x := 10
doubleValue(x)
fmt.Println(x) // 還是 10
// 使用指標:函式可以修改外部的值
func doublePointer(n *int) {
*n = *n * 2
}
y := 10
doublePointer(&y)
fmt.Println(y) // 20
Go 是值傳遞的語言——函式參數是原始值的複本。如果你想讓函式修改外部的值,或者避免複製大型結構體,就需要用指標。
new 函式
// new 分配記憶體並回傳指標
p := new(int) // *int,指向零值 0
*p = 42
格式化輸出
fmt 套件提供豐富的格式化選項:
name := "Jack"
age := 30
pi := 3.14159
// 常用格式化動詞
fmt.Printf("%s is %d years old\n", name, age) // 字串和整數
fmt.Printf("Pi = %.2f\n", pi) // 浮點數(2 位小數)
fmt.Printf("Type: %T\n", pi) // 印出型別
fmt.Printf("Value: %v\n", pi) // 通用格式
fmt.Printf("Hex: %x\n", 255) // 十六進位
fmt.Printf("Binary: %b\n", 10) // 二進位
fmt.Printf("Quoted: %q\n", "hello") // 帶引號的字串
// Sprintf 回傳格式化後的字串(不印出)
msg := fmt.Sprintf("%s: %d", name, age)
練習:溫度轉換器
把學到的語法綜合起來,寫一個簡單的溫度轉換器:
package main
import "fmt"
// 攝氏轉華氏
func celsiusToFahrenheit(c float64) float64 {
return c*9/5 + 32
}
// 華氏轉攝氏
func fahrenheitToCelsius(f float64) float64 {
return (f - 32) * 5 / 9
}
func main() {
temperatures := []float64{0, 25, 37, 100}
fmt.Println("攝氏 → 華氏")
fmt.Println("============")
for _, c := range temperatures {
f := celsiusToFahrenheit(c)
fmt.Printf("%.1f°C = %.1f°F\n", c, f)
}
fmt.Println()
fmt.Println("華氏 → 攝氏")
fmt.Println("============")
fahrenheits := []float64{32, 77, 98.6, 212}
for _, f := range fahrenheits {
c := fahrenheitToCelsius(f)
fmt.Printf("%.1f°F = %.1f°C\n", f, c)
}
}
執行結果:
攝氏 → 華氏
============
0.0°C = 32.0°F
25.0°C = 77.0°F
37.0°C = 98.6°F
100.0°C = 212.0°F
華氏 → 攝氏
============
32.0°F = 0.0°C
77.0°F = 25.0°C
98.6°F = 37.0°C
212.0°F = 100.0°C
下一步
掌握了基礎語法後,下一篇我們要來學習 Go 的資料結構:Array、Slice、Map、Struct 和 Interface。這些是建構任何 Go 應用的核心組件。