rust 如何在for循环中的match arms中使用“continue”或“break”?

wfypjpf4  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(137)

我试着做一些类似的事情:

const CONF_PATHS: [&'static str; 2] = ["/etc/foo.conf", "./foo.conf"];
...
    let conf = for path in CONF_PATHS {
        let load_result = fs::read_to_string(path);
        match load_result {
            Ok(json) => {
                let parse_result: Result<Conf, serde_json::Error> = serde_json::from_str(&json);
                match parse_result {
                    Ok(conf) => conf,
                    Err(err) => {
                        warn!("conf parse failure: {}", err);
                        continue
                    }
                }
            },
            Err(err) => {
                warn!("conf load failure: {}", err);
                continue
            }
        }
    };

但编译器(可以理解)会抱怨:

error[E0308]: `match` arms have incompatible types
  --> foo/src/main.rs:34:25
   |
30 | /                 match parse_result {
31 | |                     Ok(conf) => conf,
   | |                                 ---- this is found to be of type `Conf`
32 | |                     Err(err) => {
33 | |                         warn!("conf parse failure: {}", err);
34 | |                         continue
   | |                         ^^^^^^^^ expected `Conf`, found `()`
35 | |                     }
36 | |                 }
   | |_________________- `match` arms have incompatible types

(我希望continue会像todo!()一样被处理,不需要匹配。

  • 有没有可能以任何方式组合循环和匹配?
  • 有没有一种方法可以“返回”一个值到一个封闭的作用域,而不用将整个作用域 Package 在一个函数中?

作为一个单独的函数的解决方法说明了意图,它是:

const CONF_PATHS: [&'static str; 2] = ["/etc/foo.conf", "./foo.conf"];
const DEFAULT_CONF: Conf = Conf { report_interval: 5 };

/* Load configuration, or use default configuration. */
fn load_conf_or_default() -> Conf {
    for path in CONF_PATHS {
        let load_result = fs::read_to_string(path);
        match load_result {
            Ok(json) => {
                let parse_result: Result<Conf, serde_json::Error> = serde_json::from_str(&json);
                match parse_result {
                    Ok(conf) => return conf,
                    Err(err) => {
                        warn!("conf parse failure: {}", err);
                        continue
                    }
                }
            },
            Err(err) => {
                warn!("conf load failure: {}", err);
                continue
            }
        }
    }
    DEFAULT_CONF
}
kuarbcqp

kuarbcqp1#

这里的问题是,for并不意味着要在此上下文中使用。for总是返回(),你需要它返回一个值。
你要在一个函数被应用后,在列表中寻找第一个没有错误的值,所以你可以使用find_map来代替for,就像这样:

fn load_conf_or_default() -> Conf {
    let load_conf = CONF_PATHS.iter().find_map(|&path| {
        let load_result = fs::read_to_string(path);
        match load_result {
            Ok(json) => {
                let parse_result: Result<Conf, serde_json::Error> = serde_json::from_str(&json);
                match parse_result {
                    Ok(conf) => Some(conf),
                    Err(err) => None,
                }
            },
            Err(err) => {
                None
            }
        }
    });
    match load_conf {
        Some(conf) => conf,
        None => DEFAULT_CONF,
    }
}
093gszye

093gszye2#

问题不在于continue。它实际上被视为todo!()。问题是外部match。因为它不是一个表达式,而是一个语句,所以它必须返回()。你可以例如。将其赋值给一个变量:

let v = match load_result {
    Ok(json) => {
        let parse_result: Result<Conf, serde_json::Error> = serde_json::from_str(&json);
        match parse_result {
            Ok(conf) => conf,
            Err(err) => {
                warn!("conf parse failure: {}", err);
                continue
            }
        }
    },
    Err(err) => {
        warn!("conf load failure: {}", err);
        continue
    }
};

相关问题