Option または Result を返さない関数で try!() と ? を使用するとコンパイルされないのはなぜですか? 質問する

Option または Result を返さない関数で try!() と ? を使用するとコンパイルされないのはなぜですか? 質問する

このコードはなぜコンパイルされないのでしょうか?

use std::{fs, path::Path};

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

    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");
}

これは私が受け取ったエラーです

error[E0308]: mismatched types
  --> src/main.rs:11:17
   |
11 |     for item in try!(fs::read_dir(dir)) {
   |                 ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
   |
   = note: expected type `()`
              found type `std::result::Result<_, _>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

疑問符演算子も試してみました:

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

別のエラーが発生しました:

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

Rustの以前のバージョンでは、同様のエラーが発生していました。std::ops::Carrier

try!()と は避けるべきでしょうか?? エラーを処理する最善の方法は何でしょうか? ほとんどの場合、次のようにします:

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

しかし、それをループで使用しなければならない場合for、それは完全に混乱します

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

ベストアンサー1

try!Errは自動的にsを返すマクロです。?はほぼ同じことを行う構文ですが、を実装する任意の型で動作します。Try特性。

現在ラスト 1.22.0はをOption実装しているTryので、 で使用できます?。それ以前は、?は を返す関数でのみ使用できましたResulttry!は引き続き でのみ機能しますResult

現在ラスト 1.26.0は、mainを実装する値を返すことが許可されていますTermination。それ以前は、値は返されません。

Rust 1.26.0 時点

mainを返すようにマークし、すべての「成功」ケースでResultreturn すると、元のコードは機能します。Ok(())

use std::{fs, io, path::Path};

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

    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(())
}

それ以前は

次のようにコードを変換すると、次のようになります?

use std::{error::Error, fs, path::Path};

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

    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()),
    }
}

ここでは予想外のエラー処理がたくさんあります。他の言語ではエラー処理は必要ないことが多いです。しかし、他の言語にはエラー処理が存在します。Rust ではエラー処理がわかるようになっています。エラーは次のとおりです。

entry?

反復中に IO エラーが発生する可能性があります。

path.file_name().unwrap()

すべてのパスにファイル名があるわけではありません。ファイル名のないパスは返されないunwrapため、これが可能です。read_dir

file_name.to_string_lossy()

to_strエラーをスローすることもできますが、このようにする方が適切です。このエラーは、すべてのファイル名が有効な Unicode ではないために発生します。

try!そして、?エラーを に変換して戻り値にスローしますBox::Error。実際には、問題が発生する可能性のあるすべてのものを統合したエラーを返す方が合理的です。幸いなことに、io::Errorはちょうど適切な型です。

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!"));
    }

    // ...
}

しかし、率直に言うと、このチェックはすでに に含まれているfs::read_dirので、 をif !dis.is_dir完全に削除するだけで済みます。

use std::{fs, io, path::Path};

fn print_dir_contents() -> Result<String, io::Error> {
    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()),
    }
}

おすすめ記事