为什么要试试!()和?在不返回Option或Result的函数中使用时不编译?

时间:2015-05-31 08:42:53

标签: rust

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

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
}

4 个答案:

答案 0 :(得分:43)

try!是一个自动返回Err的宏; ?语法大致相同,但它适用于实现Try特征的任何类型。

Rust 1.22.0开始,Option实施Try,因此可以与?一起使用。在此之前,?只能用于返回Result的函数。 try!继续仅与Result一起使用。

Rust 1.26.0开始,main被允许返回实现Termination的值。在此之前,它不会返回任何值。

从Rust 1.26.0开始

如果您将main标记为返回Result,然后在所有&#34;成功&#34;中返回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()),
    }
}

答案 1 :(得分:5)

ques_in_main RFC最近合并了。一旦它completed,问题中的语法确实可以正常编译并按预期工作,前提是try!()调用被?运算符替换。

答案 2 :(得分:1)

从Rust 1.26开始,Rust支持main()的返回值,因此支持在{{1}中使用错误检查运算符JSON.parse(或等效的const mockData = JSON.parse(fs.readFileSync('../express-backend/mock_data/tweet.json', 'utf8')); 宏)当?被定义为返回try!()

main()

以上编译并返回未找到文件错误(假设本地路径中不存在main())。

Rust playground example

答案 3 :(得分:0)

尽管OP的问题略有不同,但Veedrac的回答也对我有所帮助。在阅读Rust文档时,我看到了这个片段:

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

虽然在Rust Book中他们指出了main函数的中心性,如果你在其中运行它,你会得到类似的错误。如果将代码包装在处理错误的函数中,则上述代码片段可以正常工作:

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。我正在学习Rust,所以这些片段不是那么好的Rust编码:)。