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
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
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 + b→88 - In:
88
Lời Gọi Hàm Bị Trì Hoãn:
go
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 + b→88"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):
- Đầu tiên, in lời gọi bị trì hoãn cuối cùng:
text
chào thế giới từ defer
- Sau đó, in lời gọi bị trì hoãn đầu tiên:
text
từ defer: 88
Đầu Ra Hoàn Chỉnh
text
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
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
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
- Mỗi lần lặp của vòng lặp trì hoãn việc in một ký tự
- 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 - Kết quả là chuỗi được in ngược lại:
text
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! 🐹