The Propagate trait, in particular the Propagate V2 trait, would allow the introduction of return? foo
semantics, equivalent to the following:
let x = return? foo;
// same as:
let x = match foo_is_option {
Some(v) => return Ok(v),
None => (),
};
let x = match foo_is_result {
Ok(v) => return Some(v),
Err(e) => e,
}
We have at least 3 crates that would benefit from such operator. It'd be useful if the Propagate trait had the variants to PropagateReturn and PropagateNonreturn, that match to return? foo
and foo?
respectively.
The use-case is simple. Take the following function:
/// Returns the number of commits removed and the number of added between
/// from and to, respectively.
pub fn get_counts(&self, from: &str, to: &str)
-> Result<(u64, u64), GitError>
{
if self.sha256 {
assert!(NamePurpose::Commit64.is_fit(from));
assert!(NamePurpose::Commit64.is_fit(to));
} else {
assert!(NamePurpose::Commit.is_fit(from));
assert!(NamePurpose::Commit.is_fit(to));
}
let mut output = self.cmd(|args| {
args.arg("rev-list");
args.arg("--left-right");
args.arg("--count");
args.arg(format!("{}...{}", from, to));
})?;
// perf: Vec::default doesn't allocate.
let stdout = std::mem::take(&mut output.stdout);
let stdout = String::from_utf8(stdout);
match stdout.as_ref().ok().map(|x| x.trim()).filter(|x| {
x.trim_start_matches(|x| {
char::is_ascii_digit(&x)
}).trim_end_matches(|x| {
char::is_ascii_digit(&x)
}) == "\t"
}).and_then(|x| {
let (y, z) = x.split_once("\t")?;
Some((y.parse::<u64>().ok()?, z.parse::<u64>().ok()?))
}) {
Some(v) => {
return Ok(v)
},
_ => (),
}
output.stdout = match stdout {
Ok(e) => e.into_bytes(),
Err(e) => e.into_bytes(),
};
let v = vec![
OsString::from("git"),
"rev-list".into(),
"--left-right".into(),
"--count".into(),
format!("{}...{}", from, to).into(),
];
Err(GitError::new(output, v))
}
It's designed to avoid indentation, because indentation is bad. However, it's currently required to use this weird match [stuff] { Some(v) => return Ok(v), _ => (), }
approach, which would be better served with return?
. Lacking return?
the only way to avoid the indentation is to do it like this (or to use if let
but then you run into column limit). Note also that you can't just throw or_else/etc at it, because it messes with ownership in a way that or_else/etc don't like.
Propagate
is a better name because it's about simple, unbiased, early-returns/value propagation. The name Try
adds a bias to it and discourages ppl from even considering this "propagate-return" operator! But if at least 3 crates would benefit from this operator, why not have it? It's analogous to or_else
, just like ?
is analogous to and_then
, and we do have or
operators! As for the slight weirdness of try { return? foo; }
... eh, it's just syntax, the semantics would work fine. (Specifically, this snippet would convert a Result<T, T>
into a Result<T, !>
, assuming try{}
uses PropagateReturn/Ok-wrapping.)
Also, if you need a tagline for the operator: "Please try to return this value. If returning this value doesn't work, give us the error." ^^