rust - 可以在此处匹配 Result 替换为 map_err 和 "?"

标签 rust

我有一些看起来像这样的代码(非常简化的版本)。一个函数接受两个 LoadClient 类型的函数参数和 CheckApproval并返回错误或字符串。

pub struct Client {
    pub id: String,
}

pub enum MyErr {
    RequiresApproval(Client, String),
    LoadFailed,
}

pub fn authorize<LoadClient, CheckApproval>(load_client: LoadClient, check_approval: CheckApproval) -> Result<String, MyErr> 
where
   LoadClient: FnOnce(String) -> Result<Client, String>,
   CheckApproval: for<'a> FnOnce(&'a Client, &str) -> Result<&'a str, ()>,
{
    let client = load_client("hello".to_string()).map_err(|_| MyErr::LoadFailed)?;
    let permission = "something";

    // This doesn't compile
    // let authorized = check_approval(&client, permission).map_err(|_| MyErr::RequiresApproval(client, permission.to_string()))?;
    // Ok(authorized.to_string())

    // This version does
    match check_approval(&client, permission) {
        Err(_) => Err(MyErr::RequiresApproval(client, permission.to_string())),
        Ok(authorized) => Ok(authorized.to_string()),
    }
}

我想用 ?check_approval调用(如注释掉的代码所示)以获得更简单的代码并避免额外的嵌套 - Ok最终匹配中的分支实际上是一个更长的块。

不幸的是,这不能编译:
error[E0505]: cannot move out of `client` because it is borrowed
  --> src/lib.rs:19:66
   |
19 |     let authorized = check_approval(&client, permission).map_err(|_| MyErr::RequiresApproval(client, permission.to_string()))?;
   |                                     -------              ------- ^^^                         ------ move occurs due to use in closure
   |                                     |                    |       |
   |                                     |                    |       move out of `client` occurs here
   |                                     |                    borrow later used by call
   |                                     borrow of `client` occurs here

这些看起来很相似(以我未经训练的眼睛来看)。没有借用引用 client被时间退回map_err叫做?

我的主要问题:有没有办法绕过这个问题并在不使用 match 的情况下编写代码?

rust playground link .

最佳答案

虽然您的两个版本的代码在语义上是等效的,但对于编译器来说,它们实际上是完全不同的。

失败者调用 Result::map_err()带有捕获 client 值的闭包.即client移入闭包,但在调用 check_approval() 时被借用.错误就在这里,借来的值不能移动。

你可能认为这个借用应该在函数返回时结束,但事实并非如此,因为它的返回类型 Result<&'a str, ()> , 正在 'a正是借用的生命周期。借用client延长至此 'a存在。这就是您的第二个版本有效的原因:当您匹配 Result 时, 'a不扩展到 Err(())分支,只到Ok(&'a str)所以Err(())能动client自由地。

Is there a way to get round this and write the code without using match?



好吧,您正在调用 authorized.to_string()在返回 &'a str并将其转换为拥有的 String .所以,如果你能改变你的 CheckApproval约束:

CheckApproval: FnOnce(&Client, &str) -> Result<String, ()>,

问题就消失了。

如果你不能改变它,另一个选择是做 to_string()搬家前client进入闭包,在它可以造成伤害之前完成借用:

let authorized = check_approval(&client, permission)
    .map(|a| a.to_string())
    .map_err(|_| MyErr::RequiresApproval(client, permission.to_string()))?;
Ok(authorized)

关于rust - 可以在此处匹配 Result 替换为 map_err 和 "?",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59698474/

相关文章:

concurrency - 在 Rust 中实现 MultiplexStream

rust - "use of partially moved value"语句中的 "match"错误

reference - 在 RUST 中借用和捕获变量

rust - 格式化具有固定字符数的字符串

rust - 如何使用货箱列出项目的源文件?

rust - rust 性状-如何在不消耗性状的情况下使用性状?

rust - 动态选择要调用的函数,无需中间变量

rust - 运行 cargo run 时带有行号的堆栈跟踪

rust - 如何访问当前平台的文件路径分隔符?

compilation - 特征 `core::ops::Index<i32>` 未实现