gpt4 book ai didi

rust - 选项、and_then() 和元组

转载 作者:行者123 更新时间:2023-12-02 16:08:18 27 4
gpt4 key购买 nike

我相信有一种方法可以“干净地”处理这个问题,我只是不太清楚。

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 ,我认为这是因为从 branch_and_sha() 返回的选项中的“值”是一个元组。 branch_and_sha()有效地说,“如果有一个存储库,获取它的引用,如果存在,如果它同时具有名称(分支)和目标(sha),则返回带有该信息的 Option<(String, String)> - 否则返回 None 。并且报告功能想要做一些事情if所有的Repository,分支和sha 都可以找到——除此之外别无他法。(它不应该出错或 panic 。)

在某种程度上这是人为的——它是一个乐观报告函数的例子,类似于我想写的几个。我正在寻找一种干净、惯用的方法来做到这一点。重点是'几个深度和几个分支可以返回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 使用两个通用参数声明:UF (从技术上讲,还有 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和公司产生了 Option 类型的结果,你忽略了。您正在编写一段有副作用且不产生值的代码,这是明智的,但您使用的是用于返回值的功能接口(interface)。它可以 完成,但它可能不是惯用的。在意识到 () 之前,我不得不看你的代码几次。返回值不是错字。

一种选择是返回 Option<()>来自你的 report_repo . ()在里面表明我们不关心除了副作用之外的任何事情,Optionreport_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_thenmap恶作剧,但保持控制流程相对线性。 and_then它的 friend 非常适合构造值,但你的函数的要点是它应该读起来像一系列指令,而不是值的构造函数。这就是我编写该函数的方式。

fn report_repo() -> Option<()> {
let repo = Repository::open(".").ok()?;
let (branch, sha) = branch_and_sha(repo)?;
println!("Branch={} sha={}", branch, sha);
Some(())
}

? 结尾的每一行有效地说“如果这个东西是 None,现在返回 None。否则,继续。”但是粗略的看了一下代码是“open the repo, branch and sha, and then print”,这正是你想让人们一眼就看到的。

如果我们想要真正做到这一点,我们可能应该返回 Result<(), Error> , 其中Error是一些更详细的错误类型,但对于这个简单的示例片段来说,这有点过分了。

关于rust - 选项、and_then() 和元组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68735235/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com