[英]How to use higher-rank trait bounds to make a returned impl Fn more generic?
I ran across Rust's higher-rank trait bounds recently and thought I could use them to make some functions in a parser I'm writing more generic.我最近遇到了 Rust 的高级特征边界,并认为我可以使用它们在我正在编写的解析器中创建一些更通用的函数。 However, a modification I've made is giving me an error message that I can't make heads or tails of.但是,我所做的修改给了我一条错误消息,我无法做出正面或反面。
Here's what I've got right now that works:这是我现在所拥有的有效方法:
use nom::bytes::complete::is_not;
use nom::character::complete::multispace0;
use nom::combinator::verify;
use nom::error::{
ParseError,
VerboseError,
};
use nom::sequence::terminated;
use nom::IResult;
fn one_token<'a, E>(input: &'a str) -> IResult<&str, &str, E>
where
E: ParseError<&'a str>,
{
terminated(is_not(" \t\r\n"), multispace0)(input)
}
fn str_token<'a, E>(expected_string: String) -> impl Fn(&'a str) -> IResult<&str, &str, E>
where
E: ParseError<&'a str>,
{
verify(one_token, move |actual_string| {
actual_string == expected_string
})
}
This compiles.这编译。 However, my intuitions tell me that it's not necessarily great that the impl Fn
I'm returning from str_token
is bound by a lifetime parameter on str_token
.然而,我的直觉告诉我,这并不一定伟大的impl Fn
我从返回str_token
由lifetime参数约束上str_token
。 I believe having it that way could unnecessarily restrict the usefulness of the impl Fn
trait that gets returned.我相信这样做可能会不必要地限制返回的impl Fn
特征的有用性。 So I thought I could modify it to return an impl Fn
that works for any lifetime 'b
, regardless of what the lifetime of the factory function str_token_hrtb
is:所以我想我可以修改它以返回一个适用于任何生命周期'b
的impl Fn
,无论工厂函数str_token_hrtb
的生命周期是什么:
fn str_token_hrtb<'a, E>(
expected_string: String,
) -> impl for<'b> Fn(&'b str) -> IResult<&str, &str, E>
where
E: ParseError<&'a str>,
{
verify(one_token, move |actual_string| {
actual_string == expected_string
})
}
Now, the compiler is giving me these errors:现在,编译器给了我这些错误:
error[E0277]: expected a `std::ops::Fn<(&'b str,)>` closure, found `impl std::ops::Fn<(&str,)>`
--> src/main.rs:29:6
|
29 | ) -> impl for<'b> Fn(&'b str) -> IResult<&str, &str, E>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<(&'b str,)>` closure, found `impl std::ops::Fn<(&str,)>`
|
= help: the trait `for<'b> std::ops::Fn<(&'b str,)>` is not implemented for `impl std::ops::Fn<(&str,)>`
= note: the return type of a function must have a statically known size
error[E0271]: type mismatch resolving `for<'b> <impl std::ops::Fn<(&str,)> as std::ops::FnOnce<(&'b str,)>>::Output == std::result::Result<(&'b str, &'b str), nom::internal::Err<E>>`
--> src/main.rs:29:6
|
29 | ) -> impl for<'b> Fn(&'b str) -> IResult<&str, &str, E>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'b, found concrete lifetime
|
= note: the return type of a function must have a statically known size
I don't understand how to read this.我不明白如何阅读这个。 Is it saying that the trait for<'b> std::ops::...
is not implemented for the return value of verify
?是说for<'b> std::ops::...
的特征没有为verify
的返回值实现吗? If so, why not?如果是这样,为什么不呢? And why wouldn't the same issue exist for str_token
?为什么str_token
不存在相同的问题? Also, I can't find any way to interpret the second type mismatch
error message.另外,我找不到任何方法来解释第二种type mismatch
错误消息。
Can anyone give some insight on what I'm doing wrong here and what the compiler is trying to tell me?任何人都可以对我在这里做错了什么以及编译器试图告诉我的内容有所了解吗?
Update :更新:
I'm using the nom parsing library found here: https://github.com/Geal/nom/我正在使用此处找到的 nom 解析库: https : //github.com/Geal/nom/
Also, the code for the verify
function is here: https://github.com/Geal/nom/blob/851706460a9311f7bbae8e9b7ee497c7188df0a3/src/combinator/mod.rs#L459此外, verify
功能的代码在这里: https : //github.com/Geal/nom/blob/851706460a9311f7bbae8e9b7ee497c7188df0a3/src/combinator/mod.rs#L459
Another Update :另一个更新:
Decided to close this since I realized I may not have asked questions that were specific enough.决定关闭这个,因为我意识到我可能没有问足够具体的问题。
The error message is indeed less than ideal.错误信息确实不太理想。 That's because not all difficulties of the leak check step of handling the hrtb lifetimes have been ironed out yet.这是因为并非所有处理 hrtb 寿命的泄漏检查步骤的困难都已解决。 Niko has been working on it though.尼科一直致力于它虽然。
If you turn off the leak check by pass +nightly -Zno-leak-check
flag, you'll get a saner error message:如果您通过 pass +nightly -Zno-leak-check
标志关闭泄漏检查,您将收到一条更清晰的错误消息:
error: implementation of `std::ops::FnOnce` is not general enough
--> src/main.rs:27:6
|
27 | ) -> impl for<'b> Fn(&'b str) -> IResult<&str, &str, E>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `std::ops::FnOnce` is not general enough
|
= note: `impl std::ops::Fn<(&str,)>` must implement `std::ops::FnOnce<(&str,)>`
= note: ...but `std::ops::FnOnce<(&'b str,)>` is actually implemented for the type `impl std::ops::Fn<(&str,)>`
Now, why does the compiler complain about FnOnce
when you actually return Fn
?现在,当您实际返回Fn
时,为什么编译器会抱怨FnOnce
? This is me speculating but it should be reasonable.这是我的推测,但应该是合理的。 FnOnce
is a super-trait to Fn
, rustc
is saying the code doesn't even satisfy the super-trait there's no way it could satisfy the more restrained trait itself. FnOnce
是Fn
一个超级特性, rustc
说代码甚至不满足超级特性,它无法满足更克制的特性本身。
This finally makes sense now.这现在终于说得通了。 Consider the fact that verify
is defined as:考虑这样一个事实, verify
被定义为:
pub fn verify<I: Clone, O1, O2, E: ParseError<I>, F, G>(
first: F,
second: G
) -> impl Fn(I) -> IResult<I, O1, E> where
F: Fn(I) -> IResult<I, O1, E>,
G: Fn(&O2) -> bool,
O1: Borrow<O2>,
O2: ?Sized,
By asking it to be:通过要求它是:
impl for<'b> Fn(&'b str) -> IResult<&str, &str, E>
where
E: ParseError<&'a str>,
you're asking forall<'b> 'b = 'a
which simply isn't possible.你要求forall<'b> 'b = 'a
这根本不可能。
Understanding how lifetimes work in Rust is very helpful and almost mandatory.理解生命周期在 Rust 中是如何工作的非常有帮助,而且几乎是强制性的。 When writing your own code though, it is better to skip them whenever possible.但是,在编写自己的代码时,最好尽可能跳过它们。 The lifetime elide rules are there to help you do exactly that.终身省略规则可以帮助您做到这一点。 One certainly doesn't reach for hrtb lifetimes voluntarily :D一个人肯定不会自愿达到 hrtb 的生命周期:D
For example, the more idiomatic way to write your one_token
and str_token
functions could be:例如,编写one_token
和str_token
函数的更惯用的方法可能是:
fn one_token(input: &str) -> IResult<&str, &str>
{
let res = terminated(is_not(" \t\r\n"), multispace0)(input)?;
Ok(res)
}
fn str_token(input: &str, expected_string: String) -> IResult<&str, &str>
{
let res = verify(one_token, |actual_string| {
actual_string == expected_string
})(input)?;
Ok(res)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.