Pertanyaan Mengapa mencoba! () Dan? tidak dikompilasi saat digunakan di utama?


Mengapa kode ini tidak dikompilasi?

use std::io;
use std::fs;
use std::path::Path;

fn main() {
    // Open path
    let dir = Path::new("../FileSystem");

    // Check if it is a directory
    if !dir.is_dir() {
        println!("Is not a directory");
        return;
    }

    for item in try!(fs::read_dir(dir)) {
        let file = match item {
            Err(e) => {
                println!("Error: {}", e);
                return;
            }
            Ok(f) => f,
        };

        println!("");
    }

    println!("Done");
}

Ini adalah kesalahan yang saya dapatkan

error[E0308]: mismatched types
  --> src/main.rs:15:17
   |
15 |     for item in try!(fs::read_dir(dir)) {
   |                 ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
   |
   = note: expected type `()`
              found type `std::result::Result<_, _>`
   = help: here are some functions which might fulfill your needs:
           - .unwrap()
           - .unwrap_err()
           - .unwrap_or_default()
   = note: this error originates in a macro outside of the current crate

Saya pikir itu mengeluh tentang baris ini: for item in try!(fs::read_dir(dir)) 

Saya juga mencoba operator tanda tanya:

for item in fs::read_dir(dir)? {

Yang memiliki kesalahan berbeda:

error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
  --> src/main.rs:15:17
   |
15 |     for item in fs::read_dir(dir)? {
   |                 ------------------
   |                 |
   |                 the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`)
   |                 in this macro invocation
   |
   = help: the trait `std::ops::Try` is not implemented for `()`
   = note: required by `std::ops::Try::from_error`

Versi Rust sebelumnya memiliki kesalahan serupa std::ops::Carrier

Haruskah saya menghindari try!() dan ?? Apa cara terbaik untuk menangani kesalahan? Kebanyakan saya melakukannya seperti ini:

match error_prone {
    Err(e) => {
        println!("Error: {}", e);
        return;
    },
    Ok(f) => f,
};

Tetapi jika saya harus menggunakannya dalam lingkaran, itu benar-benar berantakan

for i in match error_prone {
    // match code
} {
    // loop code
}

32
2018-05-31 08:42


asal


Jawaban:


try! adalah makro yang mengembalikan Errs secara otomatis; ? adalah sintaks yang sebagian besar melakukan hal yang sama.

Seperti Rust 1.22.0, yang ? operator juga dapat digunakan dengan Option. Sebelum itu, itu dan try! hanya bisa digunakan dalam fungsi yang mengembalikan a Result

Seperti Rust 1.26.0, main diperbolehkan mengembalikan nilai yang diimplementasikan Termination. Sebelum itu, itu tidak mengembalikan nilai apa pun.

Mulai dari Rust 1.26.0

Kode asli Anda berfungsi jika Anda menandai main sebagai kembali a Result lalu kembali Ok(()) dalam semua kasus "sukses":

use std::fs;
use std::io;
use std::path::Path;

fn main() -> Result<(), io::Error> {
    // Open path
    let dir = Path::new("../FileSystem");

    // Check if it is a directory
    if !dir.is_dir() {
        println!("Is not a directory");
        return Ok(());
    }

    for item in fs::read_dir(dir)? {
        let _file = match item {
            Err(e) => {
                println!("Error: {}", e);
                return Ok(());
            }
            Ok(f) => f,
        };

        println!("");
    }

    println!("Done");
    Ok(())
}

Sebelum itu

Ini adalah bagaimana Anda dapat mengubah kode Anda untuk digunakan ?:

use std::error::Error;
use std::fs;
use std::path::Path;

fn print_dir_contents() -> Result<String, Box<Error>> {
    // Open path
    let dir = Path::new("../FileSystem");

    // Check if it is a directory
    if !dir.is_dir() {
        return Err(Box::from("Is not a directory!"));
    }

    for entry in fs::read_dir(dir)? {
        let path = entry?.path();
        let file_name = path.file_name().unwrap();
        println!("{}", file_name.to_string_lossy());
    }

    Ok("Done".into())
}

fn main() {
    match print_dir_contents() {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error: {}", e.to_string()),
    }
}

Ada banyak penanganan kesalahan di sini yang mungkin tidak Anda harapkan - bahasa lain cenderung tidak membutuhkannya! Tetapi mereka ada dalam bahasa lain - Rust hanya membuat Anda mengetahuinya. Berikut adalah kesalahannya:

entry?

Kesalahan IO bisa terjadi selama iterasi.

path.file_name().unwrap()

Tidak semua jalur memiliki nama file. Kita dapat unwrap ini karena read_dir tidak akan memberi kita jalan tanpa nama file.

file_name.to_string_lossy()

Anda juga bisa to_str dan melemparkan kesalahan, tetapi lebih baik melakukan ini. Kesalahan ini ada karena tidak semua nama file adalah Unicode yang valid.

try! dan ? membuang kesalahan ke dalam nilai kembalian, mengonversinya menjadi Box::Error. Sebenarnya lebih masuk akal untuk mengembalikan kesalahan gabungan dari semua hal yang bisa salah. Untunglah io::Error adalah tipe yang tepat:

use std::io;
...
fn print_dir_contents() -> Result<String, io::Error> {
    ...
    if !dir.is_dir() {
        return Err(io::Error::new(io::ErrorKind::Other, "Is not a directory!"));
    }
    ...
}

Terus terang, meskipun, cek ini sudah masuk fs::read_dir, sehingga Anda sebenarnya dapat menghapus if !dis.is_dir sama sekali:     gunakan std :: fs;     gunakan std :: io;     gunakan std :: path :: Path;

fn print_dir_contents() -> Result<String, io::Error> {
    // Open path
    let dir = Path::new("../FileSystem");

    for entry in fs::read_dir(dir)? {
        let path = entry?.path();
        let file_name = path.file_name().unwrap();
        println!("{}", file_name.to_string_lossy());
    }

    Ok("Done".into())
}

fn main() {
    match print_dir_contents() {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error: {}", e.to_string()),
    }
}

35
2018-05-31 10:04



Itu ques_in_main RFC baru saja digabung. Setelah itu lengkap, sintaks dalam pertanyaan itu memang akan mengkompilasi baik - baik saja dan bekerja sebagaimana dimaksud, asalkan try!() panggilan diganti dengan ? operator.


5
2017-07-27 16:32



Pada Rust 1.26, Rust mendukung nilai balik dari main (), dan dengan demikian mendukung penggunaan operator error-check ? (atau ekuivalen dengan try!() makro) dalam main() kapan main() didefinisikan untuk mengembalikan Result:

extern crate failure;
use failure::Error;
use std::fs::File;

type Result<T> = std::result::Result<T, Error>;

fn main() -> Result<()> {
    let mut _file = File::open("foo.txt")?; // does not exist; returns error
    println!("the file is open!");
    Ok(())
}

Kompilasi di atas dan mengembalikan file tidak ditemukan kesalahan (dengan asumsi foo.txt tidak ada di jalur lokal).

Contoh tempat bermain karat


1
2018-05-07 17:33



Jawaban Veedrac membantu saya juga, meskipun pertanyaan OP sedikit berbeda. Saat membaca dokumentasi Rust, saya melihat cuplikan ini:

use std::fs::File;
use std::io::prelude::*;

let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");

Meskipun dalam Rust Book mereka menunjukkan sentralitas fungsi utama, jika Anda menjalankan ini di dalamnya, Anda akan mendapatkan kesalahan serupa. Jika Anda membungkus kode di dalam fungsi yang menangani kesalahan, cuplikan tersebut bekerja:

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;

fn print_file_content() -> Result<String, Box<Error>> {
    let mut f = File::open("foo.txt")?;
    let mut contents = String::new();

    f.read_to_string(&mut contents)?;

    println!("The content: {:?}", contents);

    Ok("Done".into())
}

fn main() {
    match print_file_content() {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error: {}", e.to_string()),
    }
}

P.S. Saya belajar Rust sehingga cuplikan ini tidak dimaksudkan sebagai pengkodean Rust yang baik :)


0
2017-12-08 11:27