我有一些看起来像这样的代码(非常简化的版本)。一个函数接受两个 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/