rust - 可以将此处的结果匹配替换为 map_err 和“?”
问题描述
我有一些看起来像这样的代码(非常简化的版本)。函数接受两个类型的函数参数,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
?
解决方案
虽然您的两个版本的代码在语义上是等效的,但它们对于编译器来说实际上是完全不同的。
失败的调用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, ()>,
问题就消失了。
如果你不能改变它,另一种选择是to_string()
在将其client
移入闭包之前执行,在它造成伤害之前完成借用:
let authorized = check_approval(&client, permission)
.map(|a| a.to_string())
.map_err(|_| MyErr::RequiresApproval(client, permission.to_string()))?;
Ok(authorized)
推荐阅读
- android - JSON模型中的双重和布尔属性问题
- ruby-on-rails - 模型订单的未定义方法“aasm_state”您是说什么?aasm_state=
- r - 从半小时的日期时间创建十进制小时列
- c - C - 在向量中输入数字 0 时中断 while 循环
- c# - WPF单选按钮组如何在选择任何项目时启用文本框?
- amazon-web-services - 当代码 URL 更改但它指向的代码没有更改时,使用 AutoPublishAlias 的 SAM Lambda 自动版本控制是否有效?
- batch-file - 为什么会打印“ECHO 已关闭”?
- java - 关于LinkedList节点HashTable性能的问题
- python - py4j.protocol.Py4JError: org.apache.spark.api.python.PythonUtils.getEncryptionEnabled 在 JVM 中不存在
- python - Jupyter 使 3D matplotlib 图变得非常小