0

我正在解析这个

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

有时有路,有时没有。

我有工作代码,但我仍在尝试学习 rust 惯用的做事方式。代码:

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()线)

将处理这两种情况——即没有“路径”和“路径”不是字符串,但它没有。也一样contains_key(drive_name)

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

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

4

3 回答 3

0

这里有一些方法可能是有效的。由于您的代码非常复杂并且使用非标准 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));
    }
    
  2. 您可以在一些前期工作中使用 match 语句。我个人更喜欢这个,因为它使匹配武器非常明确,但我认为它不那么惯用:

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

我认为 1. 更惯用,但我肯定两者都见过,而且可能更多的是风格问题。组合选项当然是惯用的包含和访问。

于 2020-09-02T23:41:54.143 回答
0

有人可能会认为选项链更惯用,但更难遵循。

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<()>
于 2020-09-03T18:03:09.177 回答
0

基于对 somnium 回答的轻微按摩,我以此结束。感觉更清脆,我必须学习更多惯用的 rust

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

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

于 2020-09-09T00:10:21.923 回答