Le basi
while esegue il suo corpo finché una condizione bool resta true:
while condition {
// corpo
}
Il compilatore valuta la condizione prima di ogni iterazione:
true: esegue il corpo, poi ricontrolla.false: esce dal ciclo.
Un esempio banale tratto dagli esempi di Zero, con una condizione che è già false:
Eseguilo e scatta solo la seconda write. Il corpo del while non viene mai eseguito perché la condizione era false fin dal primo controllo.
La condizione è un bool
Come per if/else, la condizione del ciclo deve essere un bool. Zero non converte interi, stringhe o altri valori in booleani.
while count { // errore di compilazione
// ...
}
while count > 0 { // OK
// ...
}
La condizione può essere qualsiasi cosa che produca un bool: un binding, un confronto, una combinazione con &&/|| o una chiamata a funzione. Valgono le stesse regole di if.
Cicli con contatore
Il classico schema "fai qualcosa N volte" usa un contatore e un while:
let mut i = 0
while i < 10 {
// lavoro da fare
i = i + 1
}
Tre elementi: un valore iniziale, una condizione che confronta con il limite e un aggiornamento nel corpo. È lo schema che ha ogni linguaggio imperativo: Zero semplicemente non lo traveste con una parola chiave for.
Una nota sulla forma mut usata qui sopra: il modo in cui Zero scrive i binding mutabili nella tua toolchain potrebbe essere diverso nelle versioni pre-1.0. Esegui zero check --json su un piccolo test per confermare la sintassi esatta accettata dal tuo compilatore. Lo schema concettuale (contatore + condizione + aggiornamento) è la parte stabile.
Cicli infiniti
Un while la cui condizione è sempre true gira all'infinito:
while true {
// aspetta del lavoro, gestiscilo, ricomincia
}
È la forma giusta per un ciclo di eventi, un REPL o un server di lunga durata. Per uscire, termini il processo, sollevi un errore dal corpo oppure ristrutturi il codice in modo da rendere falsa la condizione.
Quando usare un ciclo
I cicli sono uno strumento da usare con parsimonia. Cose che in altri linguaggi sembrano cicli spesso in Zero hanno una forma più pulita:
- Scorrere una collezione fissa: preferisci una funzione offerta dalla libreria standard o dalla tua shape, come una funzione di supporto in stile
forEach, un fold o una visita ricorsiva. - Leggere fino alla fine dell'input: cicla su una lettura che può fallire, ma usa
checkeraisesper gestire il limite in modo pulito invece di annidare flag di stato. - Attendere una condizione con polling: valuta se il design dovrebbe invece consegnarti un segnale. I cicli di polling sono un campanello d'allarme in qualsiasi linguaggio di sistema.
Lo stesso istinto vale per gli agenti: una forma compatta e dichiarativa è più facile da analizzare di un ciclo con contatore scritto a mano, sia per le persone sia per chi genera codice.
Cicli ed effetti
Il corpo di un while, come qualsiasi altro blocco, può fare I/O, ma solo se ha accesso a una capability World (o a una sua parte). Una funzione la cui firma non menziona World può ciclare quanto vuole, ma non può scrivere nulla all'esterno. Questa proprietà vale anche nel corpo del ciclo; il ciclo non concede nuove capability.
Sembra ovvio, ma è il motivo per cui puoi mettere un while in una funzione di calcolo "pura" e sapere comunque, dalla sola firma, che non può stampare nulla né scrivere su disco.
Note di stile
Alcune piccole abitudini che ripagano:
- Mantieni evidente la condizione. Se la condizione fa un lavoro vero, spostala in una funzione o in un binding con nome, così il ciclo si legge facilmente.
- Aggiorna il contatore in fondo al corpo, non in punti sparsi. È più facile individuare gli errori di uno.
- Preferisci condizioni di uscita che puoi esprimere come condizione del ciclo stesso a flag che cambiano a metà iterazione. Meno parti in movimento.
Prossimo passo: shape
Ora hai visto il flusso di controllo di base di Zero. Il prossimo capitolo riguarda la modellazione dei dati, a partire dalle shape, i tipi prodotto di Zero simili alle struct.
Domande frequenti
Come funzionano i cicli while in Zero?
Usa while condition { ... }. La condizione viene valutata prima di ogni iterazione; se è true, il corpo viene eseguito e il ciclo ricontrolla. Se è false, il ciclo termina e l'esecuzione prosegue dopo la parentesi graffa di chiusura. La condizione deve essere un bool.
Zero ha un ciclo for?
La prima versione di Zero offre while come unico costrutto di ciclo. Il linguaggio mantiene di proposito una superficie piccola mentre si stabilizza: meno parole chiave significano meno modi per un agente di scegliere la forma sbagliata. Un for su un intervallo o una collezione potrebbe arrivare più avanti; fino ad allora, costruisci lo stesso schema con while e un contatore.
Come si scrive un ciclo con contatore in Zero?
Inizializza un contatore, esegui un while che lo confronta con il limite e aggiornalo nel corpo: let mut i = 0; while i < 10 { ...; i = i + 1 }. La sintassi della mutabilità è ancora in evoluzione nello Zero pre-1.0, quindi controlla la documentazione attuale per la forma esatta, ma lo schema while + contatore + aggiornamento è l'idioma standard per i cicli con contatore.
Esistono break o continue in Zero?
La maggior parte dei linguaggi della famiglia di Zero offre un costrutto di uscita anticipata dai cicli; la forma esatta nello Zero pre-1.0 è uno degli aspetti che potrebbero cambiare prima della 1.0. L'approccio prudente è strutturare il ciclo in modo che sia la condizione stessa a fare il lavoro, invertendola e aggiornandola perché il ciclo termini in modo naturale, invece di affidarsi a una specifica parola chiave di controllo del flusso.
Un ciclo while in Zero può durare all'infinito?
Sì: while true { ... } è un ciclo infinito. È utile per server, cicli di eventi, REPL e qualsiasi caso senza una condizione di terminazione naturale. A differenza della truthiness implicita, qui true è un letterale di tipo bool, quindi la condizione resta ben tipizzata.