rust - 选项,and_then() 和元组
问题描述
我相信有一种方法可以“干净地”处理这个问题,我只是不太明白。
use git2::Repository;
// Prints out the current branch and sha if it exists.
fn report_repo() -> () {
Repository::open(".")
.ok()
.and_then(branch_and_sha)
.and_then(|branch_sha| => { // Fails here with E0061
let (branch, sha) = branch_sha;
println!("Branch={} sha={}", branch, sha);
None
});
}
fn branch_and_sha(repo: Repository) -> Option<(String, String)> {
match repo.head().ok() {
Some(reference) => {
match (reference.name(), reference.target()){
(Some(branch), Some(sha)) => Some((branch.to_string(), sha.to_string())),
_ => None
}
},
None => None
}
}
出现的错误是E0061,我认为这是因为 Option 返回的“值”branch_and_sha()
是一个元组。branch_and_sha()
有效地说,“如果有一个存储库,获取它的引用,如果存在,如果它同时具有名称(分支)和目标(sha),则返回Option<(String, String)>
带有该信息的 an - 否则返回None
。并且报告功能想要做如果可以找到所有Repository
, 分支和 sha 的东西 - 否则什么都没有。(它不应该出错或恐慌。)
在某种程度上,这是人为的——它是一个乐观报告功能的例子,类似于我想写的几个。我正在寻找一种干净,惯用的方式来做到这一点。关键点是“几个深度和几个分支可能会返回None
,这应该会导致无操作,否则会提供特定的(叶)信息。” 具体错误是我应该如何处理该and_then
功能,很难找到类似的问题。
解决方案
首先,你有一个小错字。Rust 中的闭包不使用=>
. 所以你的闭包应该看起来更像
.and_then(|branch_sha| { // Note: No => here
let (branch, sha) = branch_sha;
println!("Branch={} sha={}", branch, sha);
None
});
那么我们得到的错误是
--> so_cleanly.rs:15:10
|
15 | .and_then(|branch_sha| {
| ^^^^^^^^ cannot infer type for type parameter `U` declared on the associated function `and_then`
|
and_then
用两个通用参数声明:U
and F
(从技术上讲,还有T
,但这是由接收器的类型决定的self
,所以我们不用担心)。现在,F
是闭包的类型,并且始终由参数确定。另一方面,U
是闭包的返回类型。
闭包必须返回一个Option<U>
. Rust 需要查看闭包并确定它的返回类型是什么。关闭返回什么?它返回None
,并且None
可以是Option<U>
任何U
存在的。Rust 不知道该使用哪一个。我们需要告诉它。我们可以在我们返回的线路上做到这None
一点
None as Option<()>
或在and_then
通话本身中。
.and_then::<(), _>(|branch_sha| { ... })
但是,编译器提出了一个非常有效的观点。and_then
和 company 产生 type 的结果Option
,你忽略它。您正在编写一段具有副作用且不产生值的代码,这是明智的,但您正在使用旨在返回值的功能接口。它可以做到,但它可能不是惯用的。()
在意识到返回值不是错字之前,我不得不查看您的代码几次。
一种选择是Option<()>
从您的report_repo
. 内部()
表示我们不关心除了副作用之外的任何事情,并且Option
让调用者report_repo
处理(或忽略)过程中发生的任何错误,而您当前的函数只是无条件地抑制所有错误。
fn report_repo() -> Option<()> {
Repository::open(".")
.ok()
.and_then(branch_and_sha)
.map(|branch_sha| {
let (branch, sha) = branch_sha;
println!("Branch={} sha={}", branch, sha);
// Implicit return of () here, which we could do explicitly if we wanted
})
}
我在这里做了一些微妙的改变。返回类型是Option<()>
现在。根据这一点,函数内的行尾没有分号(我们正在返回该值)。最后,最后一个and_then
是map
,因为最后一步不能失败,只是在 上做一些工作Some
。
这是一个改进,但它可能仍然不是我编写这个函数的方式。
相反,如果您正在执行副作用代码,请考虑使用?
operator,它确实and_then
和map
恶作剧但保持控制流相对线性。and_then
它的朋友非常适合构造值,但你的函数的重点是它应该读起来像一个指令序列,而不是一个值的构造函数。这就是我编写该函数的方式。
fn report_repo() -> Option<()> {
let repo = Repository::open(".").ok()?;
let (branch, sha) = branch_and_sha(repo)?;
println!("Branch={} sha={}", branch, sha);
Some(())
}
以 a 结尾的每一行都?
有效地表示“如果这件事是None
,请立即返回None
。否则,请继续。” 但是粗略的看一下代码是“打开 repo、branch 和 sha,然后打印”,这正是你希望人们一眼就能看到的。
如果我们想真正做到这一点,我们可能应该 return Result<(), Error>
,这Error
是一些更详细的错误类型,但这对于这个简单的示例代码片段来说太过分了。
推荐阅读
- java - 如何在 SWT 表中显示 CCombo 组合框
- java - 在另一个 imageview 和 textview 中设置列表视图项的图像和文本
- android - Android Studio - 程序类型已经存在
- spring - Spring Integration - 将文件名与网关一起使用
- android - 附加到列表视图时如何删除重复的姓名和电话号码?
- mysql - 无法识别的语句类型。(在位置 0 的“WITH”附近)即使在升级 MariaDB 版本之后
- jquery - chart.js 显示日期 1 年
- apache-spark - 火花过滤器的操作顺序为 O(1) 或 O(n)
- ios - 使用 JSONEncoder.encode() 时的 EXC_BAD_ACCESS (code=2)
- html - 调整 ng-lazyload-image 的大小