代码之家  ›  专栏  ›  技术社区  ›  J. Doe

匹配结果类型时提前退出

  •  0
  • J. Doe  · 技术社区  · 4 年前

    Result and_then 然后你 然后你 方法可以调用):

    enum Good {
        Ok,
        Good,
        VeryGood,
    }
    enum Pizza {
        Tomato,
        Pineapple,
    }
    enum Burger {
        Cow,
    }
    
    enum Food {
        Pizza(Pizza),
        Burger(Burger),
    }
    
    fn main() {}
    
    fn s(r: Result<Good, ()>) -> Result<Food, ()> {
        r.and_then(|o| {
            match o {
                // Should be called in the next and_then block
                Good::Ok => Ok(Pizza::Tomato),
                // Should be called in the next and_then block
                Good::Good => Ok(Pizza::Pineapple),
                Good::VeryGood => {
                    // I am done. Don't call the next and_then block, but rather return the whole value to the caller.
                    return Ok(Food::Burger(Burger::Cow));
                }
            }
        })
        .and_then(|p: Pizza| {
            // At this point, the closure input value should be pizza, because that's the only returned value
            Ok(Food::Pizza(p))
        })
    }
    

    Playground

    error[E0308]: mismatched types
      --> src/main.rs:30:27
       |
    30 |                 return Ok(Food::Burger(Burger::Cow));
       |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Pizza`, found enum `Food`
       |
       = note: expected type `Pizza`
                  found type `Food`
    

    我希望有办法使它编译。我可以打破这个方法 ,但也许有办法 .

    在我的真实代码中,我有更多 然后你 它将类型映射到其他类型、错误映射等,因此这是我所面临问题的简化复制路径。

    这是从我的代码库复制粘贴的代码,显示 然后你 然后你 User (尽管它目前不起作用)。一个选项是不链接块并创建单独的值,但我希望可以在闭包中直接将值返回给调用方。

    db_session
        .query_with_values(query, values)
        .map_err(|e| {
            error!("{:?}", e);
            TechnicalServerError::SERVER_RETRY
        })
        .and_then(|f| {
            f.get_body().map_err(|e| {
                error!("{:?}", e);
                TechnicalServerError::SERVER_RETRY
            })
        })
        .and_then(|b| b.into_rows().ok_or(TechnicalServerError::SERVER_RETRY))
        .and_then(|mut c| {
            if let Some(row) = c.pop() {
                User::try_from_row(row).map_err(|e| {
                    error!("{:?}", e);
                    TechnicalServerError::SERVER_INVALID
                })
            } else {
                return Ok(Login::Other(LoginResult::UNKNOWN_USER));
            }
        })
        .and_then(|u| {
            // 'u' should be of type 'user' at this point
            // some user code here...
        })
    
    1 回复  |  直到 4 年前
        1
  •  1
  •   Daniel Fath    4 年前

    看看你编辑过的例子,我已经创建了一个 approximate example :

    // ...
        .and_then(|b| b.into_rows().ok_or(TechnicalServerError::SERVER_RETRY))
        .and_then(|mut c| {
            if let Some(row) = c.pop() {
                User::try_from_row(row).map_err(|e| {
                    error!("{:?}", e);
                    TechnicalServerError::SERVER_INVALID
                })
            } else {
                return Ok(Login::Other(LoginResult::UNKNOWN_USER))
            }
        }).and_then(|u| {
            // 'u' should be of type 'user' at this point
            // some user code here...
    

    我认为有一个基本的误解 and_then 做。

    • 然后你 拿一个 Result<T, E> ,以及转换 T 进入之内 Result<U, E> FnOnce(T) -> Result<U, E> . 当你想操纵 Ok(val) 以及 Error

    • map 拿一个 结果<T,E> 以及转换 T型 变成另一种价值 U FnOnce(T) -> U ). 当你想改变的时候用这个 正常(val) 不影响 部分。

    db_session
        .query_with_values(true)
        .map_err(|e| {
            println!("{:?}", e);
            MyError::ServerRetry
        })
        .and_then(|f| f.get_body(true).map_err(|e2|{
            println!("{:?}", e2);
            MyError::ServerRetry
        }))
        .and_then(|b| b.into_rows(true).ok_or(MyError::ServerRetry))
        .and_then(|mut c|{
            if let Some(row) = c.pop() {
                User::from_row(row).map_err(|e3| {
                    println!("{:?}", e3);
                    MyError::ServerRetry
                })
            } else {
                return Ok(User{ name: "X".to_string()})
            }
        })
        .map(|mut user| {
            user.name = "CHANGED".to_string();
            user
        });
    

    Rust playground

    但是,正如您所看到的,查询的值将始终是 Result<User, Error> unwrap 但如果它遇到错误,就会惊慌失措。或者你可以使用 if let 在不惊慌失措的情况下获得价值。

        2
  •  1
  •   Ry-    4 年前

    不是干净的。我们一起去 ? 相反呢?

    fn s(r: Result<Good, ()>) -> Result<Food, ()> {
        let p = match r? {
            // Should be called in the next and_then block
            Good::Ok => Pizza::Tomato,
            // Should be called in the next and_then block
            Good::Good => Pizza::Pineapple,
            Good::VeryGood => {
                return Ok(Food::Burger(Burger::Cow));
            },
        };
    
        Ok(Food::Pizza(p))
    }