Khóa học go

Cách Xử Lý Lỗi và Sử Dụng defer trong Go Lang

0 phút đọc

Xử lý lỗi và quản lý tài nguyên là hai khía cạnh quan trọng trong lập trình. Go cung cấp các cơ chế mạnh mẽ để xử lý lỗi và quản lý tài nguyên thông qua các khái niệm như errordefer. Bài viết này sẽ cung cấp một cái nhìn tổng quan chi tiết về cách xử lý lỗi và sử dụng defer trong Go, bao gồm cú pháp, cách sử dụng, và các ví dụ minh họa cụ thể.

Xử Lý Lỗi trong Go

Khái Niệm error

Trong Go, lỗi được biểu diễn bằng kiểu dữ liệu error, là một interface có một phương thức duy nhất Error() string. Các hàm trong Go thường trả về một giá trị error để chỉ ra rằng có lỗi xảy ra.

Ví Dụ:

package main

import (
    "errors"
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(4, 2)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }

    result, err = divide(4, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

Trong ví dụ này, hàm divide trả về một giá trị error nếu có lỗi xảy ra (chia cho 0).

Tạo Lỗi Tùy Chỉnh

Bạn có thể tạo lỗi tùy chỉnh bằng cách sử dụng hàm errors.New hoặc bằng cách triển khai interface error.

Ví Dụ:

package main

import (
    "errors"
    "fmt"
)

type MyError struct {
    Msg string
}

func (e *MyError) Error() string {
    return e.Msg
}

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, &MyError{Msg: "division by zero"}
    }
    return a / b, nil
}

func main() {
    result, err := divide(4, 2)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }

    result, err = divide(4, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

Trong ví dụ này, chúng ta tạo một lỗi tùy chỉnh bằng cách triển khai interface error.

Gói fmt và Xử Lý Lỗi

Gói fmt cung cấp các hàm như fmt.Errorf để tạo lỗi với thông điệp định dạng.

Ví Dụ:

package main

import (
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide %f by zero", a)
    }
    return a / b, nil
}

func main() {
    result, err := divide(4, 2)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }

    result, err = divide(4, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

Trong ví dụ này, chúng ta sử dụng fmt.Errorf để tạo lỗi với thông điệp định dạng.

Sử Dụng defer trong Go

Khái Niệm defer

Từ khóa defer trong Go được sử dụng để trì hoãn việc thực thi một hàm cho đến khi hàm bao quanh nó kết thúc. defer rất hữu ích trong việc đảm bảo rằng các tài nguyên được giải phóng đúng cách, ngay cả khi có lỗi xảy ra.

Ví Dụ:

package main

import "fmt"

func main() {
    fmt.Println("Start")
    defer fmt.Println("Deferred")
    fmt.Println("End")
}

Trong ví dụ này, fmt.Println("Deferred") sẽ được thực thi sau fmt.Println("End").

Sử Dụng defer để Đóng Tệp

Một trong những ứng dụng phổ biến của defer là đóng tệp sau khi hoàn thành việc đọc hoặc ghi.

Ví Dụ:

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer file.Close()

    // Đọc tệp
    buf := make([]byte, 1024)
    n, err := file.Read(buf)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))
}

Trong ví dụ này, file.Close() sẽ được gọi khi hàm main kết thúc, đảm bảo rằng tệp luôn được đóng đúng cách.

Sử Dụng defer với Nhiều Lệnh

Bạn có thể sử dụng nhiều lệnh defer trong một hàm. Các lệnh defer sẽ được thực thi theo thứ tự ngược lại (LIFO - Last In, First Out).

Ví Dụ:

package main

import "fmt"

func main() {
    defer fmt.Println("First")
    defer fmt.Println("Second")
    defer fmt.Println("Third")
    fmt.Println("Main function")
}

Trong ví dụ này, các lệnh defer sẽ được thực thi theo thứ tự ngược lại: "Third", "Second", "First".

Kết Hợp Xử Lý Lỗi và defer

Bạn có thể kết hợp xử lý lỗi và defer để đảm bảo rằng các tài nguyên được giải phóng đúng cách ngay cả khi có lỗi xảy ra.

Ví Dụ:

package main

import (
    "fmt"
    "os"
)

func readFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    buf := make([]byte, 1024)
    _, err = file.Read(buf)
    if err != nil {
        return err
    }

    fmt.Println("File read successfully")
    return nil
}

func main() {
    err := readFile("example.txt")
    if err != nil {
        fmt.Println("Error:", err)
    }
}

Trong ví dụ này, file.Close() sẽ được gọi khi hàm readFile kết thúc, đảm bảo rằng tệp luôn được đóng đúng cách ngay cả khi có lỗi xảy ra.

Sử Dụng defer để Ghi Log

Bạn có thể sử dụng defer để ghi log khi một hàm bắt đầu và kết thúc, giúp theo dõi luồng thực thi của chương trình.

Ví Dụ:

package main

import "fmt"

func logStartEnd(name string) {
    fmt.Printf("Start %s\n", name)
    defer fmt.Printf("End %s\n", name)
}

func main() {
    logStartEnd("main")
    fmt.Println("Doing some work in main")
}

Trong ví dụ này, defer đảm bảo rằng thông báo "End main" sẽ được ghi khi hàm logStartEnd kết thúc.

Sử Dụng defer để Khôi Phục (Recover) từ Panic

Go cung cấp cơ chế panicrecover để xử lý các tình huống lỗi nghiêm trọng. Bạn có thể sử dụng defer để gọi recover và khôi phục từ một panic.

Ví Dụ:

package main

import "fmt"

func safeDivide(a, b int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    fmt.Println(a / b)
}

func main() {
    safeDivide(4, 2)
    safeDivide(4, 0)
    fmt.Println("Program continues...")
}

Trong ví dụ này, recover sẽ khôi phục từ panic do chia cho 0 và chương trình sẽ tiếp tục thực thi.

Kết Luận

Xử lý lỗi và quản lý tài nguyên là hai khía cạnh quan trọng trong lập trình Go. Bài viết này đã cung cấp một cái nhìn tổng quan chi tiết về cách xử lý lỗi và sử dụng defer trong Go, bao gồm cú pháp, cách sử dụng, và các ví dụ minh họa cụ thể. Hiểu rõ về cách xử lý lỗi và sử dụng defer sẽ giúp bạn viết mã Go hiệu quả và dễ bảo trì hơn.

Avatar
Được viết bởi

TechMely Team

Gợi ý câu hỏi phỏng vấn

Gợi ý bài viết

Bình luận

Chưa có bình luận nào

Khoá học javascript từ cơ bản đến chuyên sâuYoutube Techmely