Tentang apa operator tanda tanya ini?

98

Saya membaca dokumentasi untukFile :

//..
let mut file = File::create("foo.txt")?;
//..

Apa yang ada ?di baris ini? Saya tidak ingat pernah melihatnya di Rust Book sebelumnya.

Malaikat Malaikat
sumber
Perhatikan bahwa deskripsi? telah dimasukkan dalam buku 2018 doc.rust-lang.org/edition-guide/rust-2018/…
Patrik Stas

Jawaban:

145

Seperti yang Anda ketahui, Rust tidak memiliki pengecualian. Ada kepanikan, tetapi fungsinya terbatas (mereka tidak dapat membawa informasi terstruktur) dan penggunaannya untuk penanganan kesalahan tidak dianjurkan (dimaksudkan untuk kesalahan yang tidak dapat dipulihkan).

Di Rust, penanganan kesalahan menggunakan Result. Contoh tipikal adalah:

fn halves_if_even(i: i32) -> Result<i32, Error> {
    if i % 2 == 0 {
        Ok(i / 2)
    } else {
        Err(/* something */)
    }
}

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = match halves_if_even(i) {
        Ok(i) => i,
        Err(e) => return Err(e),
    };

    // use `i`
}

Ini bagus karena:

  • saat menulis kode, Anda tidak dapat secara tidak sengaja melupakan kesalahan tersebut,
  • ketika membaca kode Anda dapat langsung melihat bahwa ada potensi kesalahan di sini.

Ini kurang dari ideal, bagaimanapun, karena sangat bertele-tele. Di sinilah operator tanda tanya ?masuk.

Di atas dapat ditulis ulang sebagai:

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = halves_if_even(i)?;

    // use `i`
}

yang jauh lebih ringkas.

Apa yang ?dilakukan di sini sama dengan matchpernyataan di atas. Singkatnya: ini membongkar Resultjika OK dan mengembalikan kesalahan jika tidak.

Agak ajaib, tetapi penanganan error membutuhkan sihir untuk memotong boilerplate, dan tidak seperti pengecualian, pemanggilan fungsi mana yang mungkin atau mungkin tidak error akan segera terlihat: yang dihiasi dengan ?.

Salah satu contoh keajaiban adalah ini juga berfungsi untuk Option:

// Assume
// fn halves_if_even(i: i32) -> Option<i32>

fn do_the_thing(i: i32) -> Option<i32> {
    let i = halves_if_even(i)?;

    // use `i`
}

Ini didukung oleh sifat (tidak stabil) Try.

Lihat juga:

Matthieu M.
sumber
5
alangkah baiknya jika Anda dapat memperluas jawaban Anda sedikit, misalnya mendiskusikan bahwa jenis fungsi yang dikembalikan harus cocok dengan jenis yang Anda coba "buka", misalnya Resultatau Option.
hellow
@hellow Saya rasa itu akan menjadi pertanyaan baru sama sekali
Paul Razvan Berg
2

Ini untuk penyebaran kesalahan untuk jenis kesalahan yang dapat dipulihkan. Hasil <T, E>. Ini membuka hasil dan memberi Anda nilai batin.

Daripada menangani kasus kesalahan, Anda menyebarkannya ke kode pemanggil dan hanya menangani kasus Ok. Manfaatnya, ini menghilangkan banyak boilerplate dan membuat implementasi fungsi lebih sederhana.

snnsnn
sumber
jangan bingung dengan .unwrap()kepanikan yang sebenarnya jika terjadi kesalahan.
Yordania