在寫go時,你會發現go希望的是 “一件事情僅有一種做法的理念” 只保留了 for 這一種迴圈結構,去掉了 C 語言中的 while 和 do-while 迴圈結構,並且填平了 C 語言中 switch 分支結構中每個 case 語句都要以 break 收尾的“坑”,Go語言做了很多事,所以我們要學習的是在Go語言你應該要用什麼語言思維和慣用手法來書寫
下面有兩段,可以看看你覺得看哪一段比較好
func someThing() error {
if errorCondition1{
// 錯誤邏輯
return error1
}
// 成功邏輯
if errorCondition2{
// 錯誤邏輯
return error2
}
// 成功邏輯
return nil
}
以及下面這段
func someThing() error{
if successCondition1 {
// 成功邏輯
if successCondition2{
//成功邏輯
return nil
}else {
// 錯誤邏輯
return error2
}
}else {
// 錯誤邏輯
return error1
}
}
應該可以很容易地看出哪一段好哪一段壞
好的是第一段,因為他遵循了快樂路徑
func DoSomething(input int) (result string, err error) {
if input <= 0 {
return "", errors.New("input must be positive")
}
// 正常邏輯靠左
result = fmt.Sprintf("Processed number: %d", input)
return result, nil
}
為什麼我們會在go中特別強調這個概念呢?
因為在很多程式語言中(例如 Java、Python 或 C#),錯誤或異常通常是透過“異常拋出”來處理的。當某個異常情況發生時,程式會拋出一個異常,這會中斷目前的程式流程,然後該異常會被一個專門設計來捕捉異常的代碼區塊捕獲,像是在javascript就會使用.catch
來接錯誤
在這個 Go 函數中,當嘗試進行除以零的操作時,它不會拋出異常,而是返回一個錯誤物件。當沒有錯誤時,返回 nil 作為 error 類型的值
所以你使用這個函數的時候,你會這樣處理錯誤
result, err := devide(10,0)
if err!= nil {
fmt.Println("Error", err)
return
}
fmt.Println("Result:", result)
Go鼓勵明確地處理錯誤,而不是依賴拋出和捕捉異常的機制
當我們使用 for range 時
var a := [...]int{1,2,3,4,5}
for i, v := range a {
...
}
可以用顯示等價轉換看得更清楚
{
var a := [...]int{1,2,3,4,5}
{
i, v := 0
for i,v = range a {
...
}
}
}
i, v := 0
是在for外面被宣告的,可以說for裡面都是用同個變數
這店可以先記著
閉包是一個函數,它能夠“捕獲”或“記住”其外部範疇中的變量
var a = [...]int{1, 2, 3, 4, 5}
funcs := []func(){}
for i, v := range a {
funcs = append(funcs, func() {
fmt.Println(i, v)
})
}
for _, fn := range funcs {
fn()
}
第一個循環是用於創建閉包函數的,而第二個循環是用於調用這些函數的
第一循環
for i, v := range a {
funcs = append(funcs, func() {
fmt.Println(i, v)
})
}
定義並存儲閉包到 funcs 切片中,但這些閉包此時還沒有被執行。它們只是被存儲起來,等待後續的調用
第二循環
for _, fn := range funcs {
fn()
}
在這個循環中,每一個之前存儲的閉包都被調用一次,此時 fmt.Println(i, v) 才會執行,輸出捕獲的 i 和 v 值
但你執行完時會發現
❯ go run main.go
4 5
4 5
4 5
4 5
4 5
由於所有的閉包共享相同的循環變量,所以它們捕獲的是這些變量的最終值,因此輸出將會是5次相同的最終值,而不是順序的1到5
for i, v := range a {
i, v := i, v //新的local variable
funcs = append(funcs, func() {
fmt.Println(i, v)
})
}
每次迭代創建了新的 i 和 v 變量,這樣閉包就會捕獲這些新變量的值,而不是外部循環變數的值
在 Go 中,for range 迭代的是被迭代對象的副本,而不是原始對象本身
slice := []int{1, 2, 3}
for _, v := range slice {
v *= 10 // 這個操作不會改變 slice 中的元素
}
fmt.Println(slice) // 輸出: [1, 2, 3]
儘管我們在循環內部修改了 v,但這不會更改 slice 的原始數據。這是因為 v 是迭代時 slice 的一個元素的副本
對於切片:使用索引来修改元素
slice := []int{1, 2, 3}
for i, v := range slice {
slice[i] = v * 10
}
fmt.Println(slice) // 輸出: [10, 20, 30]
使用索引 i 來直接修改 slice 的元素。即使 v 是元素的副本,但我們並不使用它來進行修改,而是使用索引 i
對於映射:使用鍵 (key) 來修改值
m := map[int]string{
1: "one",
2: "two",
3: "three",
}
for k, v := range m {
m[k] = v + " changed"
}
fmt.Println(m) // 輸出: map[1:one changed 2:two changed 3:three changed]
總結:當你使用 for range 迭代映射時,鍵和值都是副本,但你仍然可以透過鍵直接修改映射的原始數據。