Dlaczego słowo „break” nie wymaga średnika przy kończeniu „pętli”?

Nov 26 2020

Fragment z rozdziału 3.5 Księgi rdzy:

... używamy breaksł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

5 E_net4thecurator Nov 26 2020 at 22:30

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 loops i ifnadal 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, breakjest 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 breaki returnoceniają !, 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
3 pretzelhammer Nov 26 2020 at 22:42

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 breaki continue.