可以将此处结果上的匹配替换为map_err和“?”

Sha*_*eep 5 rust

我有一些代码看起来像这样(大大简化的版本)。函数接受两个类型为LoadClientand的函数参数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()),
    }
}
Run Code Online (Sandbox Code Playgroud)

我想?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
Run Code Online (Sandbox Code Playgroud)

这些看起来很相似(在我未经训练的眼睛看来)。client到时候归还的借用引用不是被map_err调用了吗?

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

生锈游乐场链接

rod*_*igo 3

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

失败的调用Result::map_err()使用一个捕获 的值的闭包client。也就是说,client被移入闭包,但在调用 时被借用check_approval()。错误就在这里,借用的值无法移动。

您可能认为该借用应该在函数返回时完成,但事实并非如此Result<&'a str, ()>,因为它的返回类型'a恰好是该借用的生命周期。client只要存在,借用就会延长'a。这就是你的第二个版本起作用的原因:当你匹配你的 时Result'a不会扩展到Err(())分支,只扩展到Ok(&'a str),因此Err(())能够client自由移动。

有没有办法解决这个问题并在不使用的情况下编写代码match

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

CheckApproval: FnOnce(&Client, &str) -> Result<String, ()>,
Run Code Online (Sandbox Code Playgroud)

问题就消失了。

to_string()如果你无法改变这一点,另一种选择是在将 the 移入闭包之前执行此操作client,在借用造成损害之前完成借用:

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