如何减少匹配每个返回结果或选项的调用的冗长性?

vim*_*loc 2 rust

我有一个案例,需要从 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

您应该仔细阅读Rust 编程语言的错误处理部分。最简单的方法是将核心逻辑提取到另一种方法中并普遍使用Result

非常熟悉Option上的方法Resultmap诸如、、、之类map_err的方法ok_or非常有用。关键是?运算符(以前是try!)。

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")
}
Run Code Online (Sandbox Code Playgroud)

查看像快速错误这样的包,它可以让您轻松地进行自己的错误枚举。