rust - 为什么要尝试!()和?在不返回选项或结果的函数中使用时无法编译?

标签 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
}

最佳答案

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

截至Rust 1.22.0 , Option 实现了 Try,因此它可以与 ? 一起使用。在此之前,? 只能用于返回 Result 的函数。 try! 继续只与 Result 一起工作。

截至Rust 1.26.0 , main 允许返回一个实现Termination 的值。在此之前,它不返回任何值。

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

这里有很多您可能意想不到的错误处理 - 其他语言往往不需要它!但它们存在于其他语言中——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()),
    }
}

关于rust - 为什么要尝试!()和?在不返回选项或结果的函数中使用时无法编译?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46036703/

相关文章:

vector - 在向量中存储 C++ std::function 的 Rust 等价物是什么?

Rust 错误传播在传播级别上具有不同类型

c++ - 如何一起使用编译和使用多个类似的 c++ 库?

rust - 为什么 Rust 编译一个简单的程序比 gcc/clang 慢 5-10 倍?

rust - 根据编译目标操作系统,Rust 中将不同类型的值分配给变量的惯用方式是什么?

rust - 创建 Cargo 工作区时没有在 list 中指定目标

macros - 是否可以使用 "namespace"Rust 宏?

arrays - 如何声明一个结构数组并稍后对其进行初始化?

rust - 如何将 hashbrown 数据类型与 Rayon 并行迭代器一起使用?

loops - 为什么不更新使用 cycle() 创建的迭代器的可变值,导致无限循环,尽管有停止条件?