iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
SideProject30

HOW TO GO系列 第 5

05. 函式&方法 (Function & Method)

  • 分享至 

  • xImage
  •  

函式 (Function)

函式

package main

import "fmt"

func main() {
	sum := Add(5, 3)
	fmt.Println("5 + 3 =", sum) // 5 + 3 = 8

	_, s := Add4(5, 3)
	fmt.Println(s) // 5 + 3 = 8

	sum2 := Add5(1, 2, 3, 4)
	fmt.Println(sum2) // 10
}

// 單回傳值
func Add(a int, b int) int {
	return a + b
}

// 如果參數型別一樣,可以只宣告一次
func Add2(a, b int) int {
	return a + b
}

// 如果回傳值有宣告名稱,可以不用 return
func Add3(a, b int) (sum int) {
	sum = a + b
	return
}

// 多回傳值
func Add4(a, b int) (int, string) {
	s := fmt.Sprintf("%d + %d = %d", a, b, a+b)
	return a + b, s
}

// 不定長度函數
func Add5(nums ...int) int {
	sum := 0
	for _, val := range nums {
		sum += val
	}
	return sum
}

匿名函式

package main

import "fmt"

func main() {
	add := func(a, b int) int {
		return a + b
	}
	fmt.Println(add(5, 3)) // 8
}

defer 後進先出 (LIFO, Last In First Out)

package main

import "fmt"

func main() {
	defer fmt.Println("最後執行")

	fmt.Println("最先執行")

	for i := 0; i < 5; i++ {
		defer fmt.Println(i)
	}

	fmt.Println("main func 結束執行")
}

// 最先執行
// main func 結束執行
// 4
// 3
// 2
// 1
// 0
// 最後執行

通常使用時機這個函式執行完後,對其資源需做釋放時,會使用 defer

package main

import (
	"fmt"
	"os"
)

func main() {
	// 打開檔案
	file, err := os.Open(fileName)
	if err != nil {
		fmt.Println("無法打開檔案:", err)
		return
	}
	defer file.Close() // 函數執行完畢後釋放資源
    
    // 後續可以做讀取檔案內容操作
}

error

package main

import (
	"errors"
	"fmt"
)

func main() {
	result, err := divide(8, 0)
	if err != nil {
		fmt.Println("error: ", err)
	} else {
		fmt.Println(result)
	}
}

// 可以自行拋出 error,因此在設計函數時可以一併考慮是否該設計進去
func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("除數不能為零")
	}
	return a / b, nil
}

error 建議一定要處理或往上層拋,Go 其中一個設計理念就是希望開發者能對每一個 error 作處理,所以可以發現 Go 是沒有 try catch 的關鍵字。(這裡指的 error,是可預期的錯誤

這也變相要求開發者必須清楚知道哪個流程會拋出錯誤,我覺得這是好事,所以我是支持這個設計

(呃,社群好像反對或支持這個設計的聲音都有,所以也不能排除未來可能會有 try catch

有些錯誤不是在編譯或是程式設計中可預期的錯誤,屬於執行錯誤 (runtime error) 就會拋出 panic

package main

import "fmt"

func main() {
	fmt.Println("執行")
	// 觸發 panic
	panic("panicccc")
	// fmt.Println("不會執行")
}

發生 panic 時已經算是嚴重的錯誤,通常就不處理了。理想上如果能預期錯誤的話,就應該要被包裝成 error,程式設計師再針對此錯誤做特定處理。

但是這也不是絕對的,像是一個服務其中有個功能有問題,但是不能因為這個功能導致整個服務異常時,可以使用 recover

package main

import "fmt"

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover from: ", r)
		}
	}()

	fmt.Println("執行")
	// 觸發 panic
	panic("panicccc")
	// fmt.Println("不會執行")
}

error handle 是一個重要且值得討論的議題,需要謹慎面對,但是這裡就先不展開討論。

只有天真的程式設計師會相信讀寫不會失敗

方法 (Method)

方法

package main

import "fmt"

type Rectangle struct {
	W  float64
	H float64
}

func (r Rectangle) Area() float64 {
	return r.W * r.H
}

func (r Rectangle) Perimeter() float64 {
	return 2*r.W + 2*r.H
}

func main() {
	rect := Rectangle{W: 5, H: 3}

	area := rect.Area()
	perimeter := rect.Perimeter()

	fmt.Println(area)
	fmt.Println(perimeter)
}

上一篇
04. 組合型別
下一篇
06. 介面 (Interface)
系列文
HOW TO GO30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言