为什么要试试!()和? 在不返回选项或结果的函数使用时不能编译? 为什么要试试!()和? 在不

2019-05-10 12:27发布

为什么这个代码不能编译?

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`

锈之前的版本有关类似错误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
}

Answer 1:

try! 是返回宏Err S也可以自动; ? 是语法,做多是同样的事情,但它实现的任何类型的工作Try特质。

由于锈1.22.0 , Option执行Try ,所以它可以搭配? 。 在此之前, ? 只能在返回函数中使用Resulttry! 继续只与工作Result秒。

由于锈1.26.0 , main被允许返回一个实现价值Termination 。 在此之前,它不返回任何值。

防锈1.26.0的

如果您标记您的原始代码的工作main是返回一个Result ,然后返回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()),
    }
}

有很多的错误处理,你可能没有想到的 - 其他语言不倾向于需要它! 但他们在其他语言中存在 - 拉斯特只是让你知道。 下面是错误的:

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


Answer 2:

该ques_in_main RFC得到了最近合并。 一旦它的完成 ,在这个问题的语法确实会编得很好,像预期的那样,只要try!()的调用与替换? 运营商。



Answer 3:

作为防锈1.26,锈病支持从主()的返回值,并因此支持使用错误校验操作者的? (或等价的try!()宏)在main() ,当main()被定义为返回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(())
}

上述编译并返回一个文件未找到错误(假设foo.txt不存在于本地路径存在)。

拉斯特操场例子



Answer 4:

Veedrac的回答帮我过,虽然OP的问题略有不同。 在阅读了锈的文档,我看到这个片段:

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

尽管在锈书他们指出主要功能的中心,如果你运行这个里面,你会得到一个类似的错误。 如果你换一个功能处理上述片段工作中的错误中的代码:

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

PS我正在学习锈病所以这些片段是不打算作为良好的防锈编码:)



文章来源: Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
标签: rust