提问人:Sridhar Ratnakumar 提问时间:7/12/2023 最后编辑:Sridhar Ratnakumar 更新时间:7/13/2023 访问量:93
如何用嵌套的“match”和返回“Result”的守卫来简化这个 Rust 代码?
How to simplify this Rust code with nested `match` with guards returning `Result`s?
问:
有没有更简单的方法来编写这个 Rust 代码?有关完整上下文,请参阅此 PR。
impl FromArgValue for FlakeRef {
fn from_arg_value(s: &str) -> std::result::Result<FlakeRef, String> {
match Url::parse(s) {
Ok(url)
if url.scheme() == "https" && url.host() == Some(Host::Domain("github.com")) =>
{
match url.path_segments().map(|c| c.collect::<Vec<_>>()) {
None => Ok(FlakeRef::Flake(s.to_string())),
Some(paths) => match paths[..] {
[user, repo, "pull", pr_] => match pr_.parse() {
Ok(pr) => Ok(FlakeRef::GithubPR {
owner: user.to_string(),
repo: repo.to_string(),
pr: pr,
}),
Err(_) => Ok(FlakeRef::Flake(s.to_string())),
},
_ => Ok(FlakeRef::Flake(s.to_string())),
},
}
}
_ => Ok(FlakeRef::Flake(s.to_string())),
}
}
}
编辑(7 月 12 日):根据下面的答案,我将其进一步简化为以下内容。还有什么可以进一步简化的吗?
use try_guard::guard;
/// Parse a Github PR URL into its owner, repo, and PR number
pub fn parse_url(url: &String) -> Option<(String, String, u64)> {
let url = Url::parse(url).ok()?;
guard!(url.scheme() == "https" && url.host() == Some(Host::Domain("github.com")));
let paths = url.path_segments().map(|c| c.collect::<Vec<_>>())?;
match paths[..] {
[user, repo, "pull", pr_] => {
let pr = pr_.parse::<u64>().ok()?;
Some((user.to_string(), repo.to_string(), pr))
}
_ => None,
}
}
fn from_arg_value(s: &str) -> std::result::Result<FlakeRef, String> {
match github::PullRequest::parse_url(&s.to_string()) {
Some((owner, repo, pr)) => Ok(FlakeRef::GithubPR {
owner: owner,
repo: repo,
pr: pr,
}),
None => Ok(FlakeRef::Flake(s.to_string())),
}
}
答:
1赞
drewtato
7/12/2023
#1
您可以轻松地将其转换为返回并用作错误路径的函数。这将允许您使用 try 运算符提前返回。然后,您可以将两条路径转换为 。Option
None
?
Ok
fn from_arg_value(s: &str) -> Result<FlakeRef, String> {
fn from_arg_value_internal(s: &str) -> Option<FlakeRef> {
let url = Url::parse(s).ok()?;
if url.scheme() == "https" && url.host() == Some(Host::Domain("github.com")) {
return None;
}
let path_segments = url.path_segments()?;
let paths: Vec<_> = path_segments.collect();
let [user, repo, "pull", pr_] = paths[..] else {
return None;
};
let pr = pr_.parse().ok()?;
Some(FlakeRef::GithubPR {
owner: user.to_string(),
repo: repo.to_string(),
pr,
})
}
let flake = from_arg_value_internal(s).unwrap_or_else(|| FlakeRef::Flake(s.to_string()));
Ok(flake)
}
通常,唯一需要匹配 or 的时间是在两个分支中执行复杂逻辑时。否则,将有一个方法可以在更少的代码中工作。您可以通过阅读非常相似的 Option
或 Result
文档来找到这些文档。Option
Result
另一件事是,您可以通过重复调用来避免创建。Vec
next
let mut path_segments = url.path_segments()?;
let user = path_segments.next()?;
let method = path_segments.next()?;
let pr_ = path_segments.next()?;
if method != "pull" || path_segments.next().is_some() {
return None;
}
评论
0赞
Kaplan
7/12/2023
#2
这种方法更接近原始程序,并根据 BallpointBen 的建议根据排除程序工作:
fn from_arg_value(s: &str) -> std::result::Result<FlakeRef, String> {
'end: {
let Ok(url) = Url::parse(s) else { break 'end; };
if url.scheme() != "https" || url.host() != Some(Host::Domain("github.com")) { break 'end; }
let Some(paths) = url.path_segments().map(|c| c.collect::<Vec<_>>()) else { break 'end; };
let [user, repo, "pull", pr_] = paths[..] else { break 'end; };
let Ok(pr) = pr_.parse() else { break 'end; };
return Ok(FlakeRef::GithubPR {
owner: user.to_string(),
repo: repo.to_string(),
pr,
});
}
Ok(FlakeRef::Flake(s.to_string()))
}
评论
let else
.map_err
?
match