【问题标题】:Are nested matches a bad practice in idiomatic Rust?嵌套匹配在惯用的 Rust 中是一种不好的做法吗?
【发布时间】:2016-07-22 09:26:00
【问题描述】:

我有一个get_url_content 函数并且不关心错误(这只是一个测试)。它返回一个Option<String>

extern crate hyper;

use std::io::Read;
use hyper::client::Client;

fn get_url_content(url: &str) -> Option<String> {
    let client = Client::new();
    let mut s = String::new();

    match client.get(url).send() {
        Ok(mut res) => {
            match res.read_to_string(&mut s) {
                Ok(_) => {
                    Some(s)
                },
                Err(_) => {
                    None
                }
            }
        },
        Err(_) => {
            None
        }
    }
}

这个函数很好用,但我觉得它不容易阅读。我认为有一些关于这种案例的最佳实践可以使其更具可读性。嵌套匹配是否是一种不好的做法(如 JS 中的回调地狱),如果是,如何避免?

【问题讨论】:

    标签: rust


    【解决方案1】:

    ResultOption 有一些很棒的方法可以让这样的代码更简单。

    fn get_url_content(url: &str) -> Option<String> {
        let client = Client::new();
    
        let res = client.get(url)
                        .send()
                        .ok()  // Convert to Option, discarding error
                        .and_then(|mut res| {
                            let mut s = String::new();
                            let result = res.read_to_string(&mut s);
                            result.ok().map(|_| s)
                         }
               })
    }
    

    我建议查看ResultOption 的文档;它们之间的大多数转换组合都有方法,并根据成功或错误一半进行操作。

    如果您可以将get_url_content 更改为返回Result,我建议您这样做(请参阅the error handling documentation。使用您自己的错误类型和From 的一些实现,则该函数变为(

    fn get_url_content(url: &str) -> Result<String, MyError> {
        let client = Client::new();
        let mut s = String::new();
    
        let got = try!(client.get(url));
        let sent = try!(got.send());
        try!(sent.read_to_string(s));
        Ok(s)
    }
    

    使用新的? 运算符可能更简单。

    【讨论】:

      【解决方案2】:

      让事情变得更简洁的最简单方法是去掉一些大括号:

      match client.get(url).send() {
          Ok(mut res) =>
              match res.read_to_string(&mut s) {
                  Ok(_) => Some(s),
                  Err(_) => None,
              },
          Err(_) => None,
      }
      

      内部匹配可以更简洁地表达为

      match client.get(url).send() {
          Ok(mut res) =>
              res.read_to_string(&mut s).ok().map(|_| s),
      
          Err(_) => None,
      }
      

      这建议在外部类型上使用map(以获取Result&lt;Option&lt;_&gt;, _&gt;),然后使用.unwrap_or(None).unwrap_or_default() 删除结果

      client.get(url).send()
            .map(|mut res| res.read_to_string(&mut s).ok().map(|_| s))
            .unwrap_or(None)
      

      【讨论】:

      • .map(|mut res| res.read_to_string(&amp;mut s).ok().map(|_| s)) 不是因为s 被可变借用并在同一个表达式中返回而失败吗?
      • 它有效。感谢这个分步回答,它帮助我更好地理解 Rust。
      • @ChrisEmerson 不,因为输出的生命周期不依赖于借用。
      • @rap-2-h 我应该提到我最喜欢的是第二个版本——最后一个只是用两个函数调用替换了两个 match 分支。
      猜你喜欢
      • 2014-04-30
      • 2011-08-22
      • 1970-01-01
      • 2012-10-06
      • 1970-01-01
      • 2019-08-01
      • 2013-06-05
      • 1970-01-01
      • 2019-02-12
      相关资源
      最近更新 更多