Dlaczego słowo „break” nie wymaga średnika przy kończeniu „pętli”?
Fragment z rozdziału 3.5 Księgi rdzy:
... używamy
break
słowa kluczowego z wartościącounter * 2
. Po pętli używamy średnika, aby zakończyć instrukcję przypisującą wartośćresult
.
Plus fragment kodu:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
Rozumiem, jak to działa i dlaczego wynikiem jest 20, ale zauważyłem, że jeśli usunę średnik z wiersza zawierającego słowo kluczowe „break”, program będzie równoważny.
Dlaczego w tym przypadku średnik jest opcjonalny?
Odpowiedzi
Krótszy przykład:
let _: i32 = loop {
if true {
break 3; // ()
}
};
To tylko kolejny przykład, w którym średnik nie wpływa na zamierzony wynik. Po pierwsze, wstawienie średnika wprowadza wyrażenie wyrażenia, którego wynikiem jest typ jednostki ()
. Ponieważ wyrażenia loop
s i if
nadal dopuszczają blok kodu oceniający do tego samego typu ()
, wszystkie typy są zgodne.
let _: i32 = loop {
if true {
break 3 // !
}
};
Jeśli średnik zostanie usunięty, break
jest oceniany jako typ nigdy! , który wymusza na każdym innym typie. Oznacza to, że spełni każdy typ oczekiwany przez zakres zewnętrzny. Więc wszystko jest w porządku, o ile nie próbujesz dołączać żadnych innych instrukcji przed końcem bloku if.
Oba break
i return
oceniają !
, ponieważ ich skutki uboczne sugerują, że program nie przejdzie przez naturalny przepływ pracy.
Zobacz też:
- Dlaczego wyrażenia zwracające używają średników, gdy są niepotrzebne?
- Jaka jest różnica między użyciem instrukcji return a pominięciem średnika w Rust?
- Jak statycznie potwierdzić koniec funkcji jest nieosiągalny
Dokumentacja języka Rusta dotycząca instrukcji wyrażeń :
Oświadczenie wyrażenie to taki, który ocenia wyrażenie i ignoruje jego wynik. Z reguły celem wyrażenia jest wywołanie skutków oceny jego wyrażenia.
Wyrażenie, które składa się tylko z wyrażenia blokowego lub wyrażenia przepływu sterowania, jeśli jest używane w kontekście, w którym instrukcja jest dozwolona, może pomijać końcowy średnik .
Wyobrażam sobie, że dotyczy to wyłącznie estetyki i ergonomii, ponieważ prawie wszystko jest wyrażone w Rust. Gdyby końcowy średnik był obowiązkowy po wszystkich wyrażeniach, musielibyśmy zakończyć bloki if-else (które również są wyrażeniami) średnikami, co wygląda okropnie:
if {
// do something
} else {
// do something else
}; // <- gross
Podobnie możemy pominąć końcowy średnik we wszystkich wyrażeniach przepływu sterowania, ponieważ generują one przepływ sterowania, więc typowa funkcja średnika, która polega na odrzuceniu wyniku wyrażenia i ()
zamiast tego oszacowanie , staje się nieistotna.
fn five() -> i32 {
return 5 // to semicolon or not to semicolon? it doesn't matter
}
W powyższym przykładzie nie ma znaczenia, czy zakończymy return 5
średnikiem, czy nie, ponieważ i tak nic nie może „przechwycić” wyniku tego wyrażenia, ponieważ generuje przepływ sterowania. To samo dotyczy innych wyrażeń przepływu sterowania, takich jak break
i continue
.