Menu

Цикл while в Golang: как написать while и do-while в Go

В Go нет ключевого слова while. Цикл for с одним только условием и есть while в Go, for без условия это бесконечный цикл, а do-while это бесконечный цикл с проверкой в конце.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

В Go нет ключевого слова while. Напишите for и после него условие, и получится цикл while:

Условие проверяется перед каждой итерацией, поэтому если оно ложно с самого начала, тело не выполнится ни разу. Это то же самое, что while (balance < 2000) в C, Java или JavaScript, только без скобок и ключевого слова. Создатели Go выбрали одно ключевое слово для всех видов циклов; все четыре формы разобраны на странице о цикле for.

while(true): бесконечный цикл

Уберите условие совсем, и цикл будет работать, пока что-то внутри его не завершит:

Идиоматичная запись for { }. for true { } тоже компилируется, но выглядит так, будто автор пришёл из другого языка.

Бесконечный цикл завершают четыре вещи: break (выход из цикла), return (выход из функции), паника и os.Exit. Программа, чей main крутится в for {} без всего этого, работает, пока её не убьют, и для сервера или воркера это именно то, что нужно.

do-while

do-while выполняет тело один раз, прежде чем проверить условие. Отдельного синтаксиса в Go нет. Поставьте проверку в конец бесконечного цикла:

Тело выполняется хотя бы один раз в любом случае, и это определяющее свойство do-while. Источник случайных чисел использует фиксированное зерно, поэтому вывод одинаков при каждом запуске; для настоящей случайности используйте rand.IntN без источника с зерном.

Вторая форма оставляет условие в заголовке, принудительно выполняя первый проход:

for first := true; first || condition(); first = false {
	// body
}

Это работает, но бесконечный цикл с break в конце читается проще, и так делает большая часть кода на Go.

Чтение до конца

Самый частый цикл while в реальном коде на Go читает ввод, пока тот не закончится. bufio.Scanner создан для этого: Scan возвращает false в конце ввода или при ошибке, поэтому он и служит условием цикла:

Замените strings.NewReader(input) на os.Stdin, и тот же цикл будет читать ввод пользователя построчно. После цикла проверьте scanner.Err(): по false из Scan нельзя понять, закончился ввод нормально или с ошибкой.

break и continue

Оба работают в любой форме for:

В цикле только с условием continue сразу переходит к проверке условия. Отсюда легко получить тонкий баг: если счётчик обновляется внизу тела, continue выше пропускает обновление, и цикл никогда не заканчивается. Обновляйте счётчик в начале, как выше, или используйте for из трёх частей, где инструкция post выполняется и после continue.

Ожидание чего-либо

Цикл, который ждёт выполнения условия, должен блокироваться или спать, а не крутиться вхолостую:

// Wrong: burns a CPU core while waiting
for !ready {
}

// Better: poll with a delay
for !isReady() {
	time.Sleep(100 * time.Millisecond)
}

Опрос обычной переменной, в которую пишет другая горутина, ещё и гонка данных. Между горутинами лучше ждать на канале, sync.WaitGroup или sync.Cond, либо использовать select с таймером. Для циклов повторных попыток с лимитом предпочитайте счётный for, чтобы цикл не мог работать вечно:

for attempt := 1; attempt <= 5; attempt++ {
	if err := connect(); err == nil {
		break
	}
	time.Sleep(time.Duration(attempt) * 200 * time.Millisecond)
}

Частые ошибки

  • Условие никогда не меняется. for i < 10 { fmt.Println(i) } не закончится никогда. В теле каждого цикла while должно быть что-то, что приближает его к выходу.
  • Написать while. while x < 5 {} даёт syntax error: unexpected name x at end of statement, потому что while не ключевое слово, и компилятор читает его как обычное имя.
  • Скобки по привычке. for (x < 5) {} компилируется, а gofmt убирает скобки.
  • Объявление переменной в цикле. for n := 10; n > 0 {} это синтаксическая ошибка: цикл с инструкцией инициализации требует всех трёх частей. Объявите n до цикла или напишите полную форму из трёх частей.

Часто задаваемые вопросы

Есть ли в Go цикл while?

Как ключевого слова нет. Go использует for для всех циклов, а for с одним только условием это ровно цикл while:

for count < 10 {
	count++
}

Условие проверяется перед каждой итерацией, так же как while (count < 10) в C или Java.

Как написать while(true) в Go?

Напишите for без ничего после него: for { ... }. Цикл крутится, пока его не завершат break, return, паника или os.Exit. for true { ... } тоже компилируется, но идиоматичная форма for { }.

Как написать цикл do-while в Go?

Используйте бесконечный цикл и поставьте проверку условия в конце тела:

for {
	// body runs at least once
	if !condition {
		break
	}
}

Тело всегда выполняется один раз до проверки условия, а это и есть смысл do-while.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ