Trong Go, lỗi được xử lý theo cách khác so với nhiều ngôn ngữ lập trình khác, thay vì sử dụng các cơ chế như ngoại lệ (exceptions), Go sử dụng giá trị trả về (return value) để truyền đạt thông tin về lỗi. Việc xử lý lỗi thông qua việc kiểm tra giá trị trả về cho phép lập trình viên kiểm soát tốt hơn và rõ ràng hơn cách xử lý lỗi. Dưới đây là các cách xử lý lỗi trong Go:

1. Sử dụng giá trị trả về kiểu error

Trong Go, các hàm thường trả về một hoặc nhiều giá trị, và một trong những giá trị đó có thể là kiểu error để biểu thị lỗi (nếu có). error là một interface trong Go, và hàm Error() trong interface này trả về chuỗi mô tả lỗi.

Ví dụ:

package main

import (
	"errors"
	"fmt"
)

// Hàm thực hiện phép chia, trả về lỗi nếu mẫu số là 0
func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("mẫu số không thể bằng 0")
	}
	return a / b, nil
}

func main() {
	result, err := divide(10, 0)
	if err != nil {
		fmt.Println("Lỗi:", err)
	} else {
		fmt.Println("Kết quả:", result)
	}
}

Trong ví dụ này, hàm divide trả về hai giá trị: kết quả của phép chia và một giá trị lỗi (nếu có). Nếu có lỗi (mẫu số bằng 0), lỗi được trả về và được xử lý trong hàm main().

2. Tạo lỗi tùy chỉnh bằng errors.New()

Go cung cấp cách dễ dàng để tạo ra lỗi bằng cách sử dụng hàm errors.New(), giúp bạn tạo ra một đối tượng error với thông điệp mô tả lỗi.

Ví dụ:

import "errors"

err := errors.New("Đã xảy ra lỗi")

3. Sử dụng fmt.Errorf để bổ sung thêm thông tin cho lỗi

Go cung cấp hàm fmt.Errorf để tạo lỗi với chuỗi định dạng, tương tự như Printf, để cung cấp thông tin chi tiết hơn về lỗi.

Ví dụ:

import "fmt"

err := fmt.Errorf("không thể kết nối đến %s: %v", "localhost", err)

4. Sử dụng panicrecover (Quản lý lỗi nghiêm trọng)

  • panic: Khi có một lỗi nghiêm trọng mà bạn không thể hoặc không muốn xử lý, bạn có thể sử dụng panic để dừng chương trình. panic thường được sử dụng khi có những lỗi không thể phục hồi được (unrecoverable errors), ví dụ như lỗi logic trong chương trình.
  • recover: Được sử dụng để bắt và xử lý panic. Nó giúp khôi phục chương trình từ một trạng thái hoảng loạn và cho phép chương trình tiếp tục chạy.

Ví dụ:

func doPanic() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Chương trình đã khôi phục từ panic:", r)
		}
	}()
	panic("Đã xảy ra lỗi nghiêm trọng!")
}

func main() {
	doPanic()
	fmt.Println("Chương trình vẫn tiếp tục chạy sau panic.")
}

5. Sử dụng kiểu lỗi tùy chỉnh

Ngoài errors.New(), bạn cũng có thể tạo kiểu lỗi tùy chỉnh để thêm ngữ cảnh chi tiết hơn.

Ví dụ:

type MyError struct {
	Code    int
	Message string
}

func (e *MyError) Error() string {
	return fmt.Sprintf("Lỗi %d: %s", e.Code, e.Message)
}

func throwError() error {
	return &MyError{404, "Không tìm thấy tài nguyên"}
}

func main() {
	err := throwError()
	if err != nil {
		fmt.Println(err)
	}
}

Kết quả:

Lỗi 404: Không tìm thấy tài nguyên

Tóm tắt:

  • Sử dụng kiểu error để truyền tải lỗi trong Go.
  • Kiểm tra giá trị trả về của hàm có thể chứa lỗi.
  • Tạo lỗi tùy chỉnh bằng errors.New() hoặc fmt.Errorf.
  • Dùng panicrecover để xử lý lỗi nghiêm trọng (unrecoverable).
  • Có thể tạo các kiểu lỗi tùy chỉnh để chứa thông tin lỗi phức tạp hơn.

Nhờ cách tiếp cận này, Go giúp lập trình viên kiểm soát rõ ràng hơn về cách xử lý và quản lý lỗi trong chương trình.