Reputation: 53
I am using crate clap
v4。When I try to write something validating arguments against regex, I had some problem with lifetimes.
My code as following:
pub fn validator_regex(r: &'static str) -> ValueParser {
ValueParser::from(move |s: &str| -> std::result::Result<&str, Error> {
let reg = regex::Regex::new(r).unwrap();
match reg.is_match(s) {
true => Ok(s),
false => Err(Error::from(format!("not matches {}", r))),
}
})
}
pub fn validator_identifier() -> ValueParser {
validator_regex("^[-_0-9a-zA-Z]+$")
}
And compilation error:
error: lifetime may not live long enough
--> main\./src\cmd\cmd_util.rs:440:21
|
437 | ValueParser::from(move |s: &str| -> std::result::Result<&str, Error> {
| - - let's call the lifetime of this reference `'2`
| |
| let's call the lifetime of this reference `'1`
...
440 | true => Ok(s),
| ^^^^^ returning this value requires that `'1` must outlive `'2`
Could any help me on these two questions:
lifetime may not live long enough
error in this code, since the returned &str
lives as long as the closure argument, and this can be compiled normallypub fn validator_regex(r: &'static str) -> impl Fn(&str) -> Result<&str, String> {
move |s: &str| -> Result<&str, String> {
let reg = regex::Regex::new(r).unwrap();
match reg.is_match(s) {
true => Ok(s),
false => Err(format!("not match {}", r)),
}
}
}
Upvotes: 4
Views: 2732
Reputation: 71350
Let's take a look at the From
impl you're invoking:
impl<P> From<P> for ValueParser
where
P: TypedValueParser + Send + Sync + 'static,
Ok, take a look at TypedValueParser
:
impl<F, T, E> TypedValueParser for F
where
F: Fn(&str) -> Result<T, E> + Clone + Send + Sync + 'static,
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
T: Send + Sync + Clone,
So, the signature is not Fn(&str) -> Result<&str, E>
, it is <T> Fn(&str) -> Result<T, E>
. Is it different? Can't we just put &str
in the place of T
?
It is very different. Let's annotate the lifetimes:
// Fn(&str) -> Result<&str, E>
for<'a> Fn(&'a str) -> Result<&'a str, E>
// <T> Fn(&str) -> Result<T, E>
<&'b str> for<'a> Fn(&'a str) -> Result<&'b str, E>
Where does 'b
come from? It can't be HRTB, as it is declared in the impl
header (T
) and not the trait bound (Fn
). Therefore, it must be a fixed lifetime. See the mismatch? The lifetimes I called 'a
and 'b
are what the compiler calls '1
and '2
, respectively. If 'b
is fixed, it cannot be derived from the dynamic HRTB 'a
.
The fix is simple: just don't return &str
, return String
instead:
pub fn validator_regex(r: &'static str) -> ValueParser {
ValueParser::from(move |s: &str| -> std::result::Result<String, Error> {
let reg = regex::Regex::new(r).unwrap();
match reg.is_match(s) {
true => Ok(s.to_owned()),
false => Err(Error::from(format!("not matches {}", r))),
}
})
}
Upvotes: 2