如何减少匹配每个返回结果或选项的调用的详细程度?

问题描述 投票:2回答:1

我有一个案例,我需要从TOML文件中提取一些数据。它的工作正常,但绝大多数代码都匹配Results或Options。

use std::env;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::exit;

extern crate toml;

fn main() {
    // Get the path of the config file
    let homedir = match env::home_dir() {
        Some(path) => path,
        None       => {
            println!("Error: Could not find home directory");
            exit(1);
        }
    };
    let mut config_path = PathBuf::from(homedir);
    config_path.push(".relay");
    config_path.set_extension("toml");

    // Open the config file
    let mut file = match File::open(&config_path) {
        Ok(file) => file,
        Err(why) => {
            println!("Error opening {}: {}", config_path.display(),
                                             Error::description(&why));
            exit(1);
        },
    };

    // Read the contents of the config file into memory
    let mut config_str = String::new();
    match file.read_to_string(&mut config_str) {
        Ok(_)    => (),
        Err(why) => {
            println!("Couldn't read {}: {}", config_path.display(),
                                             Error::description(&why));
            exit(1);
        }
    }

    // Parse the toml
    let config: toml::Value = match config_str.parse() {
        Ok(config) => config,
        Err(errs)   => {
            println!("Error Parsing config file:");
            for err in &errs {
                println!("  {}", Error::description(err));
            }
            exit(1);
        }
    };

    let host = match config.lookup("relay.host") {
        Some(host) => match host.as_str() {
            Some(s) => s.to_string(),
            None    => {
                println!("Error: 'host' option is not a valid string");
                exit(1);
            }
        },
        None       => {
            println!("Error: 'host' option not found under [relay] block");
            exit(1);
        }
    };
    println!("{}", host);
}

这似乎相当冗长,当我开始从文件中提取更多数据时,它会变得更糟。有什么我想念的东西会让这个更干净吗?我知道我可以使用unwrap()expect()替换大多数匹配语句,但是如果出现问题我想打印一些更漂亮的错误消息,并避免使用以下内容:

thread '<main>' panicked at '<actual error message>', ...
note: Run with `RUST_BACKTRACE=1` for a backtrace
rust
1个回答
4
投票

你应该仔细阅读error handling section of The Rust Programming Language。最简单的方法是将逻辑的核心提取到另一个方法并且普遍使用Result

非常熟悉OptionResult的方法。像mapmap_errok_or这样的方法非常有用。 lynchpin是?运营商(以前是try! macro)。

use std::env;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;

extern crate toml;

fn inner_main() -> Result<(), Box<Error>> {
    let mut config_path = env::home_dir().ok_or("Could not find home directory")?;

    config_path.push(".relay");
    config_path.set_extension("toml");

    let mut file = File::open(&config_path)
        .map_err(|e| format!("Could not open {}: {}", config_path.display(), e))?;

    let mut config_str = String::new();
    file.read_to_string(&mut config_str)
        .map_err(|e| format!("Couldn't read {}: {}", config_path.display(), e))?;

    let config: toml::Value = config_str
        .parse()
        .map_err(|e| format!("Error parsing config file: {}", e))?;

    let relay = config.get("relay").ok_or("[relay] block not found")?;

    let host = relay
        .get("host")
        .ok_or("'host' option not found under [relay] block")?;

    let host = host.as_str()
        .map(ToString::to_string)
        .ok_or("'host' option is not a valid string")?;

    println!("{}", host);

    Ok(())
}

fn main() {
    inner_main().expect("Error")
}

查看像quick-error这样的包,让您轻松制作自己的错误枚举。

© www.soinside.com 2019 - 2024. All rights reserved.