将“键存在”与“以及如果键类型正确”组合的惯用方式

问题描述

我正在解析

[xxxxx]
drive0={}
drive1={path="xxxx"}
...

有时候有一条路,有时没有。

我有工作代码,但我仍在尝试学习生锈的惯用方式。代码

for i in 0..8 {
    let drive_name = format!("drive{}",i);
    if dmap.contains_key(&drive_name) {
        if let Some(d) = config[drive_name].as_table() {
            this.units.push(Rkunit::new(true));
            if d.contains_key("path") {
                if let Some(path) = d["path"].as_str() {
                    let file = Openoptions::new()
                        .read(true)
                        .write(true)
                        .create(true)
                        .open(path)
                        .unwrap();
                    this.units[i].file.replace(file);
                }
            }
        } else {
            this.units.push(Rkunit::new(false));
        }
    }
}

    

我希望

if let Some(path) = d["path"].as_str()

(即没有if d.contains()行)

将处理两种情况-即没有“ path”和“ path”不是字符串,但是没有。也与contains_key(drive_name)相同。

我尝试了各种语法猜测,以查看是否可以避免嵌套另一个if并且可以找到一个

那么,有没有更好的方法呢?关于解析toml的其他任何评论

解决方法

这里有一些方法可能是有效的。由于您的代码非常复杂并且使用非标准API,因此很难看出我的更改是否有用:

  1. 使用常规代码结构,但将.contains组合在一起并将所包含的值应用到函数.get(...).map(...)中。 x.get(y)返回一个Option值,该值使您可以访问整个Option API,而x[y]与之不同,if let Some(d) = config.get(&drive_name).map(|c| c.as_table()) { this.units.push(Rkunit::new(true); if let Some(path) = d.get("path").and_then(String::as_str) { } } else { this.units.push(Rkunit::new(false)); } 会在密钥不存在时惊慌。

    let drive = config.get(&driver_name); // return an option
    let path = drive.map(|d|.get("path")); // returns an option
    match (drive,path) {
        (Some(d),Some(p)) => {
            this.units.push(Rkunit::new(true));
            let file = OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                .open(path)
                .unwrap();
            this.units[i].file.replace(p);
        }
        (Some(d),None) => {
            this.units.push(Rkunit::new(true);
        }
        _ => {
            this.units.push(Rkunit::new(false);
        }
    }
    
  2. 您可以使用match语句进行一些准备工作。我个人比较喜欢这种方式,因为它可以使比赛方式非常明确,但是我认为这种方式不太习惯:

    Module not found: Error: Can't resolve './src/js/index.js' in 'C:\Users\Arian\Desktop\Daily Planner 2.0'
     @ multi babel-polyfill ./src/js/index.js main[1]
    
    ERROR in multi (webpack)-dev-server/client?http://localhost:8081 babel-polyfill ./src/js/index.js
    Module not found: Error: Can't resolve './src/js/index.js' in 'C:\Users\Arian\Desktop\Daily Planner 2.0'
     @ multi (webpack)-dev-server/client?http://localhost:8081 babel-polyfill ./src/js/index.js main[2]
    

我认为1.更惯用,但我当然看过两者,而且可能更多是风格问题。当然,撰写选项在包含和访问方面是惯用的。

,

有些人可能认为期权的链接更为惯用,但很难遵循。

config.get(&driver_name)
    .or_else(|| {                            // no drive,passing None all the way down
        this.units.push(Rkunit::new(false));
        None
    })
    .and_then(|drive| {                      // having a drive,trying to get a path
        this.units.push(Rkunit::new(true)); 
        drive.as_table().get("path")
    })
    .map(|path| {                            // only having a path,we're doing the thing
        let file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(path.as_str())             // as_str is there
            .unwrap();
        this.units[i].file.replace(file);
    });
// also "unused Option" warning,because map returns an Option<()>
,

基于对massage的答案的轻微按摩,我就此结束了。感觉更脆了,我必须学习更多的惯用锈

      for i in 0..8 {
            let drive_name = format!("drive{}",i);

            if let Some(drive) = dmap.get(&drive_name).and_then(|x| x.as_table()) {
                this.units.push(Rkunit::new(true));
                if let Some(path) = drive.get("path").and_then(|x| x.as_str()) {
                    let file = OpenOptions::new()
                        .read(true)
                        .write(true)
                        .create(true)
                        .open(path)
                        .unwrap();
                    this.units[i].file.replace(file);
                }
            } else {
                this.units.push(Rkunit::new(false));
            }
        }

我知道任何配置错误都会被忽略。但这就是我所追求的。如果通过->稍后给出了非法路径,则可能不应该爆炸。