0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Hiểu Rõ Về `defer` Trong Go: Thực Hành Tốt Nhất và Lỗi Thường Gặp

Đăng vào 7 tháng trước

• 4 phút đọc

Hiểu Rõ Về Câu Lệnh defer Trong Go

Bạn đã bao giờ viết mã Go chưa? Chắc chắn rồi - đó là lý do bài viết này dành cho bạn! Nếu câu trả lời của bạn là có, thì bạn có thể đã gặp từ khóa defer. Hãy cùng tìm hiểu chi tiết về nó để viết mã Go tốt hơn (cuối cùng, với tư cách là những lập trình viên Go, mục tiêu của chúng ta là viết mã hoạt động hiệu quả và không mắc phải những lỗi mà các ngôn ngữ khác có thể gặp phải, haha).


Vậy defer Là Gì?

Nếu bạn tìm kiếm định nghĩa chính thức, nó sẽ nói rằng: "defer lên lịch cho một lời gọi hàm sẽ chạy ngay sau khi hàm bao quanh trả về". Vậy điều này có nghĩa là gì? Nói đơn giản, điều này có nghĩa là bất kỳ lời gọi hàm nào được đặt trước defer sẽ được thêm vào một ngăn xếp các lời gọi bị trì hoãn và sẽ được thực thi ngay trước khi hàm cha (nơi mà defer được khai báo) kết thúc.

Các lời gọi bị trì hoãn sẽ được thực hiện theo thứ tự LIFO (Last-In, First-Out).

defer Hoạt Động Như Thế Nào?

Hãy cùng xem một ví dụ mã giúp chúng ta hiểu rõ hơn và làm sáng tỏ về cách thực thi LIFO mà chúng ta đã đề cập trước đó. Dưới đây là một mã Go đơn giản với một số phép cộng và các câu lệnh in. Tôi nghĩ rằng đây là một ví dụ đơn giản nhưng hiệu quả để hiểu defer mà không dùng từ ngữ phức tạp.

go Copy
package main

import "fmt"

func main() {
    a := 54
    b := 34
    c := a + b
    fmt.Println(c)  // In tổng ngay lập tức
    defer fmt.Printf("từ defer: %v\n", a + b)  
    defer fmt.Println("chào thế giới từ defer")
    fmt.Printf("Xin chào, Thế giới!\n")
}

Bạn đoán xem đầu ra sẽ là gì? Hãy xem đầu ra trước:

text Copy
88
Xin chào, Thế giới!
chào thế giới từ defer
từ defer: 88

Tại Sao và Cách Điều Này Xảy Ra

Hãy phân tích luồng thực thi của câu lệnh defer trong Go từng bước một.

Thực Thi Từng Bước

Thực Thi Ngay Lập Tức:

  • Tính toán c = a + b88
  • In: 88

Lời Gọi Hàm Bị Trì Hoãn:

go Copy
defert fmt.Printf("từ defer: %v\n", a + b)
defert fmt.Println("chào thế giới từ defer")

Các tham số được đánh giá ngay lập tức:

  • a + b88
  • "chào thế giới từ defer" đã sẵn sàng
  • Các hàm tự thân được đưa vào ngăn xếp để thực thi sau này

Thực Thi Thông Thường Tiếp Tục:

  • In: Xin chào, Thế giới!

Các Hàm Bị Trì Hoãn Thực Thi Theo Thứ Tự Đảo Ngược (LIFO):

  1. Đầu tiên, in lời gọi bị trì hoãn cuối cùng:
text Copy
   chào thế giới từ defer
  1. Sau đó, in lời gọi bị trì hoãn đầu tiên:
text Copy
   từ defer: 88

Đầu Ra Hoàn Chỉnh

text Copy
88
Xin chào, Thế giới!
chào thế giới từ defer
từ defer: 88

Những Điều Chính Cần Ghi Nhớ

  • Các tham số cho các hàm bị trì hoãn được đánh giá khi defer được gặp
  • Các hàm bị trì hoãn thực thi theo thứ tự Last-In-First-Out (LIFO)
  • Các hàm bị trì hoãn chạy ngay trước khi hàm bao quanh trả về

Các Trường Hợp Sử Dụng Thường Gặp: Xử Lý Tệp

go Copy
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Lỗi khi mở tệp:", err)
        return
    }

    // Đảm bảo tệp được đóng khi hàm kết thúc
    defer file.Close()

    fmt.Println("Tệp đã mở thành công.")
}

👉 Ở đây, file.Close() được đảm bảo sẽ chạy ở cuối main(), ngay cả khi hàm trả về sớm do lỗi. Điều này rất hữu ích trong việc dọn dẹp tài nguyên và đảm bảo tệp được đóng sau khi sử dụng.

Được Rồi, Chúng Ta Đã Hiểu defer, Bây Giờ Hãy Làm Một Điều Thú Vị

Một mẹo thú vị với defer là đảo ngược một chuỗi mà không cần sử dụng vòng lặp hoặc mảng bổ sung. Bởi vì các hàm bị trì hoãn thực thi theo thứ tự Last-In-First-Out (LIFO), chúng ta có thể tận dụng điều này để in các ký tự theo thứ tự ngược lại.

go Copy
package main

import "fmt"

func main() {
    word := "golang"
    fmt.Print("Chuỗi đảo ngược: ")

    for i := 0; i < len(word); i++ {
        ch := word[i] // lấy ký tự ngay lập tức
        defer fmt.Printf("%c", ch)
    }
}

Cách Nó Hoạt Động

  1. Mỗi lần lặp của vòng lặp trì hoãn việc in một ký tự
  2. Các lời gọi bị trì hoãn được xếp chồng lên nhau, vì vậy chúng thực thi theo thứ tự ngược lại khi main() kết thúc
  3. Kết quả là chuỗi được in ngược lại:
text Copy
Chuỗi đảo ngược: gnalog

Kết Luận

Hy vọng rằng bài viết này đã giúp bạn hiểu rõ về defer một cách dễ dàng và thú vị!

Chúc bạn lập trình vui vẻ với Go! 🐹

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

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

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