繁体   English   中英

如何使用更高等级的特征边界使返回的 impl Fn 更通用?

[英]How to use higher-rank trait bounds to make a returned impl Fn more generic?

我最近遇到了 Rust 的高级特征边界,并认为我可以使用它们在我正在编写的解析器中创建一些更通用的函数。 但是,我所做的修改给了我一条错误消息,我无法做出正面或反面。

这是我现在所拥有的有效方法:

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
    })
}

这编译。 然而,我的直觉告诉我,这并不一定伟大的impl Fn我从返回str_token由lifetime参数约束上str_token 我相信这样做可能会不必要地限制返回的impl Fn特征的有用性。 所以我想我可以修改它以返回一个适用于任何生命周期'bimpl 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
    })
}

现在,编译器给了我这些错误:

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

我不明白如何阅读这个。 是说for<'b> std::ops::...的特征没有为verify的返回值实现吗? 如果是这样,为什么不呢? 为什么str_token不存在相同的问题? 另外,我找不到任何方法来解释第二种type mismatch错误消息。

任何人都可以对我在这里做错了什么以及编译器试图告诉我的内容有所了解吗?

更新

我正在使用此处找到的 nom 解析库: https : //github.com/Geal/nom/

此外, verify功能的代码在这里: https : //github.com/Geal/nom/blob/851706460a9311f7bbae8e9b7ee497c7188df0a3/src/combinator/mod.rs#L459

另一个更新

决定关闭这个,因为我意识到我可能没有问足够具体的问题。

错误信息确实不太理想。 这是因为并非所有处理 hrtb 寿命的泄漏检查步骤的困难都已解决。 尼科一直致力于虽然。

如果您通过 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,)>`

现在,当您实际返回Fn时,为什么编译器会抱怨FnOnce 这是我的推测,但应该是合理的。 FnOnceFn一个超级特性, rustc说代码甚至不满足超级特性,它无法满足更克制的特性本身。

这现在终于说得通了。 考虑这样一个事实, 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,

通过要求它是:

impl for<'b> Fn(&'b str) -> IResult<&str, &str, E>
where
    E: ParseError<&'a str>,

你要求forall<'b> 'b = 'a这根本不可能。


理解生命周期在 Rust 中是如何工作的非常有帮助,而且几乎是强制性的。 但是,在编写自己的代码时,最好尽可能跳过它们。 终身省略规则可以帮助您做到这一点。 一个人肯定不会自愿达到 hrtb 的生命周期:D

例如,编写one_tokenstr_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM