Go không có từ khóa while. Viết for rồi một điều kiện là bạn đã có vòng lặp while:
Điều kiện được kiểm tra trước mỗi lần lặp, nên nếu ngay từ đầu nó đã sai thì thân vòng lặp không chạy lần nào. Cách này giống hệt while (balance < 2000) trong C, Java hay JavaScript, chỉ bỏ đi cặp ngoặc tròn và từ khóa. Những người thiết kế Go chọn một từ khóa duy nhất cho mọi dạng vòng lặp; xem trang vòng lặp for để biết cả bốn dạng.
while(true): vòng lặp vô hạn
Bỏ hẳn điều kiện đi, vòng lặp sẽ chạy cho đến khi có thứ gì đó bên trong kết thúc nó:
for { } là cách viết chuẩn. for true { } cũng biên dịch được, nhưng đọc lên như thể người viết quen tay từ một ngôn ngữ khác.
Có bốn cách kết thúc một vòng lặp vô hạn: break (thoát khỏi vòng lặp), return (thoát khỏi hàm), panic và os.Exit. Một chương trình có main nằm trong for {} mà không có cách nào ở trên sẽ chạy cho đến khi bị tắt, và đó lại đúng là điều bạn muốn ở một máy chủ hay worker.
do-while
do-while chạy thân vòng lặp một lần rồi mới kiểm tra điều kiện. Go không có cú pháp riêng cho nó. Hãy đặt phần kiểm tra ở cuối một vòng lặp vô hạn:
Dù thế nào thân vòng lặp cũng chạy ít nhất một lần, và đó là đặc điểm làm nên do-while. Nguồn số ngẫu nhiên dùng seed cố định nên đầu ra giống nhau ở mọi lần chạy; muốn ngẫu nhiên thật thì dùng rand.IntN không qua nguồn có seed.
Dạng thứ hai giữ điều kiện trên phần đầu vòng lặp bằng cách ép lượt chạy đầu tiên:
for first := true; first || condition(); first = false {
// body
}
Cách này vẫn chạy được, nhưng vòng lặp vô hạn có break ở cuối dễ đọc hơn, và phần lớn code Go viết theo kiểu đó.
Đọc cho đến khi hết
Vòng lặp while phổ biến nhất trong code Go thực tế là đọc đầu vào cho đến khi hết. bufio.Scanner được thiết kế cho đúng việc này: Scan trả về false khi hết đầu vào hoặc khi gặp lỗi, nên chính nó là điều kiện của vòng lặp:
Đổi strings.NewReader(input) thành os.Stdin là cùng vòng lặp đó sẽ đọc dữ liệu người dùng nhập vào theo từng dòng. Nhớ kiểm tra scanner.Err() sau vòng lặp: việc Scan trả về false không cho bạn biết đầu vào kết thúc bình thường hay bị lỗi.
break và continue
Cả hai đều dùng được trong mọi dạng của for:
Trong vòng lặp chỉ có điều kiện, continue nhảy thẳng về phần kiểm tra điều kiện. Điều này dễ sinh ra một bug khó thấy: nếu biến đếm được cập nhật ở cuối thân vòng lặp, một continue nằm phía trên sẽ bỏ qua lần cập nhật đó và vòng lặp không bao giờ dừng. Hãy cập nhật biến đếm ở đầu thân vòng lặp như ví dụ trên, hoặc dùng for ba mệnh đề, vì câu lệnh post của nó vẫn chạy cả sau continue.
Chờ một điều gì đó
Vòng lặp chờ một điều kiện nên chặn (block) hoặc ngủ, chứ không quay liên tục:
// Wrong: burns a CPU core while waiting
for !ready {
}
// Better: poll with a delay
for !isReady() {
time.Sleep(100 * time.Millisecond)
}
Kiểm tra liên tục một biến thường mà goroutine khác đang ghi còn là data race. Giữa các goroutine, hãy chờ trên một channel, một sync.WaitGroup hay một sync.Cond, hoặc dùng select với một timer. Với vòng lặp thử lại có giới hạn, nên dùng for có đếm để vòng lặp không thể chạy mãi:
for attempt := 1; attempt <= 5; attempt++ {
if err := connect(); err == nil {
break
}
time.Sleep(time.Duration(attempt) * 200 * time.Millisecond)
}
Lỗi thường gặp
- Quên thay đổi biến trong điều kiện.
for i < 10 { fmt.Println(i) }không bao giờ dừng. Vòng lặp while nào cũng cần thứ gì đó trong thân đẩy nó dần tới điểm thoát. - Viết
while.while x < 5 {}báosyntax error: unexpected name x at end of statement, vìwhilekhông phải từ khóa; trình biên dịch đọc nó như một cái tên bình thường. - Thêm ngoặc tròn theo thói quen.
for (x < 5) {}vẫn biên dịch được, vàgofmtsẽ xóa cặp ngoặc đi. - Khai báo biến ngay trong vòng lặp.
for n := 10; n > 0 {}là lỗi cú pháp; vòng lặp có mệnh đề khởi tạo phải có đủ cả ba mệnh đề. Hãy khai báontrước vòng lặp, hoặc viết đủ dạng ba mệnh đề.
Câu hỏi thường gặp
Go có vòng lặp while không?
Không có từ khóa riêng. Go dùng for cho mọi vòng lặp, và một for chỉ có điều kiện chính là vòng lặp while:
for count < 10 {
count++
}
Điều kiện được kiểm tra trước mỗi lần lặp, giống hệt while (count < 10) trong C hay Java.
Viết while(true) trong Go như thế nào?
Viết for mà không có gì phía sau: for { ... }. Vòng lặp chạy mãi cho đến khi bị break, return, panic hoặc os.Exit kết thúc. for true { ... } cũng biên dịch được, nhưng for { } mới là cách viết chuẩn của Go.
Viết vòng lặp do-while trong Go như thế nào?
Dùng vòng lặp vô hạn và đặt phần kiểm tra điều kiện ở cuối thân vòng lặp:
for {
// body runs at least once
if !condition {
break
}
}
Thân vòng lặp luôn chạy một lần trước khi điều kiện được kiểm tra, và đó chính là ý nghĩa của do-while.