【问题标题】:More compact, easier to read Rust error handling更紧凑、更易读 Rust 错误处理
【发布时间】:2023-03-03 16:58:01
【问题描述】:

我是一名 Java 开发人员,正在学习 Rust。这是一个小例子,它将当前目录的文件名读入字符串列表/向量,然后输出。

Java 与 nio:

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
    
public class ListFileNames {
    
    public static void main(String[] args) {
        final Path dir = Paths.get(".");
        final List<String> fileNames = new ArrayList<>();
        try {
            final Stream<Path> dirEntries = Files.list(dir);
            dirEntries.forEach(path -> {
                if (Files.isRegularFile(path)) {
                        fileNames.add(path.getFileName().toString());
                }
            });
        }
        catch (IOException ex) {
            System.err.println("Failed to read " + dir + ": " + ex.getMessage());
        }
    
        print(fileNames);
    }
    
    private static void print(List<String> names) {
        for (String name : names) {
            System.out.println(name);
        }
    }
}

这是我在 Rust 中提出的:

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

fn main() {
    let dir = Path::new(".");
    let mut entries: Vec<String> = Vec::new();
    let dir_name = dir.to_str().unwrap();
    let dir_content = fs::read_dir(dir);
    match dir_content {
        Ok(dir_content) => {
            for entry in dir_content {
                if let Ok(dir_entry) = entry {
                    if let Ok(file_type) = dir_entry.file_type() {
                        if file_type.is_file() {
                            if let Some(string) = dir_entry.file_name().to_str() {
                                entries.push(String::from(string));
                            }
                        }
                    }
                }
            }
        }
        Err(error) => println!("failed to read {}: {}", dir_name, error)
    }

    print(entries);
}

fn print(names: Vec<String>) {
    for name in &names {
        println!("{}", name);
    }
}

有没有办法减少 Rust 代码中过多的缩进,使其更易于阅读,类似于 Java 代码?

【问题讨论】:

标签: rust


【解决方案1】:

可以使用? 来短路执行,正如@Bazaim 所展示的那样,尽管这在语义上略有不同,因为它会在第一个错误时停止,而不是忽略它。

为了忠实于您的语义,您将改用 基于流的Iterator-based 方法,如can be seen here

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

fn main() -> Result<(), Box<dyn Error>> {
    let dir = Path::new(".");
    let dir_content = fs::read_dir(dir)?;

    dir_content.into_iter()
        .filter_map(|entry| entry.ok())
        .filter(|entry| if let Ok(t) = entry.file_type() { t.is_file() } else { false })
        .filter_map(|entry| entry.file_name().into_string().ok())
        .for_each(|file_name| println!("{}", file_name));

    Ok(())
}

我对@9​​87654326@ 步骤不太非常满意,但我确实喜欢函数式 API 清晰地分隔每个步骤的事实。

【讨论】:

    【解决方案2】:

    我也是 Rust 的初学者。
    ? 运算符非常有用。
    这是我得到的最小版本:

    use std::error::Error;
    use std::fs;
    use std::path::Path;
    
    fn main() -> Result<(), Box<dyn Error>> {
        let dir = Path::new(".");
        let mut entries: Vec<String> = Vec::new();
        let dir_content = fs::read_dir(dir)?;
    
        
        for entry in dir_content {
            let entry = entry?;
            if entry.file_type()?.is_file() {
                if let Some(string) = entry.file_name().to_str() {
                    entries.push(String::from(string));
                }
            }
        }
    
        print(entries);
    
        Ok(())
    }
    
    fn print(names: Vec<String>) {
        for name in &names {
            println!("{}", name);
        }
    }
    

    【讨论】:

    • 是否有可能以某种方式“捕获”错误,因此在 10 日发生错误时打印已收集的文件名?
    • 由于? 几乎是throwTry / Catch 不存在,我们不能在我的代码中使用Err 做任何事情。相反,Matthieu M. 代码将正确完成这项工作。
    • @ThomasS.:您不需要捕获打印直到错误:您只需将entries.push(String::from(string))println!("{}", string) 交换。只有当您想继续过去需要重构代码的错误时;例如,将循环体提取到fn get_file_name(entry: &amp;io::Result&lt;DirEntry&gt;) -&gt; Result&lt;String, Box&lt;dyn Error&gt;&gt; 将允许在get_file_name 内使用短路?,同时能够匹配循环本身中的Result :)
    【解决方案3】:

    @Bazaim 答案的略微修改版本。

    因此,您可以在单独的函数中编写易出错的代码并将其引用传递给向量。

    如果失败,您可以打印错误并仍然打印您传入的向量中的名称。

    use std::error::Error;
    use std::fs;
    use std::path::Path;
    
    fn main() {
        let mut names = vec![];
        if let Err(e) = collect(&mut names) {
            println!("Error: {}", e);
        }
        for name in names {
            println!("{}", name);
        }
    }
    
    fn collect(names: &mut Vec<String>) -> Result<(), Box<dyn Error>>  {
        let dir = Path::new(".");
        let dir_content = fs::read_dir(dir)?;
    
        for entry in dir_content {
            let entry = entry?;
            if entry.file_type()?.is_file() {
                if let Some(string) = entry.file_name().to_str() {
                    names.push(String::from(string));
                }
            }
        }
    
        Ok(())
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-12
      • 2017-07-03
      • 2021-07-01
      • 1970-01-01
      • 2020-10-10
      • 1970-01-01
      • 2013-09-06
      • 1970-01-01
      相关资源
      最近更新 更多