Rust 惯用的组合方式';密钥存在';与';如果是正确的类型';解析toml

Rust 惯用的组合方式';密钥存在';与';如果是正确的类型';解析toml,rust,toml,Rust,Toml,我正在分析这个 [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[dr

我正在分析这个

[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()
(如果d.包含()行,则不带

将处理这两种情况-即没有“路径”和“路径”不是字符串,但它不是。与
相同,也包含\u键(驱动器名称)

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


那么,有没有更好的方法,或者这是最好的方法呢。欢迎对解析toml的任何其他评论。

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

  • 使用您的通用代码结构,但组合使用
    .contains
    ,并将包含的值应用于模式
    .get(…).map(…)
    x.get(y)
    返回一个选项值,允许您访问整个选项API,这与
    x[y]
    不同,如果密钥不存在,它会死机

    if let Some(d)=config.get(&drive_name).map(|c|c.as_table()){
    this.units.push(Rkunit::new(true);
    如果让Some(path)=d.get(“path”)。然后(String::as_str){
    }
    }否则{
    this.units.push(Rkunit::new(false));
    }
    
  • 你可以在一些准备工作中使用match语句。我个人更喜欢这样,因为它使match arms非常明确,但我认为它没有那么惯用:

    let drive=config.get(&driver_name);//返回一个选项
    让path=drive.map(|d |.get(“path”);//返回一个选项
    匹配(驱动器、路径){
    (一些(d),一些(p))=>{
    this.units.push(Rkunit::new(true));
    让file=OpenOptions::new()
    .读(真)
    .写(真)
    .create(true)
    .open(路径)
    .unwrap();
    这个.units[i].file.replace(p);
    }
    (部分(d),无)=>{
    this.units.push(Rkunit::new(true);
    }
    _ => {
    this.units.push(Rkunit::new)(false);
    }
    }
    

  • <>我想1。这是更习惯的,但我确实看到了这两个词,这可能更像是一个风格的问题。作曲选项当然是惯用的包含和访问。

    < P>有人可能认为选择链接更习惯,但更难追随。

    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<()>
    
    config.get(&driver\u name)
    .或| |{//没有车,一路都没有经过
    this.units.push(Rkunit::new(false));
    没有一个
    })
    .然后(| drive |{//有一个驱动器,试图找到一条路径
    this.units.push(Rkunit::new(true));
    drive.as_table().get(“路径”)
    })
    .map(| path |{//只有一条路径,我们正在做这件事
    让file=OpenOptions::new()
    .读(真)
    .写(真)
    .create(true)
    .open(path.as_str())//as_str在那里
    .unwrap();
    this.units[i].file.replace(文件);
    });
    //还有“未使用的选项”警告,因为映射返回一个选项
    
    基于对索曼纽姆回答的轻微推敲,我以这个结尾。它感觉更脆,我学到了一些更地道的语言

          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));
                }
            }
    

    我知道任何配置错误都会被默默地忽略。但这正是我想要的。如果给出了非法路径,可能不应该爆炸->稍后->非标准API是什么意思?@pm100非标准API:基本上使用非标准API,所以我无法在rust Playder中测试它,因为我无法访问您正在使用的API。谢谢,v有用。我不知道我可以在元组上进行那种奇特的匹配。但是第一个对我来说似乎最容易遵循。好吧,我尝试合并你的#1,但它无法编译。我不明白为什么不。rust抱怨第三行的d.get是错误的。说d是一个选项,我不知道。因为第一个应该是“未选择”的e config.get.(我修复了第2行中的小错误)@pm100它可能取决于
    as_table()
    返回的内容。在我的示例中,它假设
    as_table()
    返回某种类似于HashMap的API。例如,工作正常。检查as_table()的API返回并在第二个if语句中相应地使用它。@JohnKugelman它不使用
    和_then
    ,区别只在于必须返回
    一些(())
    显然,我不会称之为惯用语。函数式调用链不应该真的有副作用。在这种情况下,遵守函数式惯用语会带来更大的代码膨胀