如何在Rust中将`Fn`特征与`impl trait`返回类型一起使用?

时间:2019-12-30 18:08:54

标签: rust

我想使用返回类型为Fn的{​​{1}}特征。 例如:

impl Trait

但是,此代码无法使用以下错误消息进行编译:

let funcs: [&Fn(&str) -> impl Iterator<Item = &str>] =
    [&str::split_whitespace, &str::split_ascii_whitespace];

我应该怎么办?

2 个答案:

答案 0 :(得分:0)

除了impl Trait现在只能在有限的语法上使用的事实外,从语义上说,这仅意味着该位置将存在一种具体类型。它不是异构类型或动态调度的许可证。

这可以完成,但是很快变得笨拙:

type StrFn<'a> = &'a dyn Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>>;

fn main() {
    let f1: StrFn = &|s: &'static str| Box::new(s.split_whitespace());
    let f2: StrFn = &|s: &'static str| Box::new(s.split_ascii_whitespace());
    let fs = vec![f1, f2];

    fs[0]("rust 2020").for_each(|s| println!("{}", s));
}

也许有更好的方法。

答案 1 :(得分:0)

str::split_whitespacestr::split_ascii_whitespace具有不同的返回类型。您不能构造各种类型的数组。相反,您可以创建boxed trait objects的数组。这将在运行时确定要调用的特定方法的地方执行动态调度(而不是在编译时知道特定方法版本的静态调度)

本质上,目标是使所有功能的签名为:

for<'a> fn(&'a str) -> Box<dyn Iterator<Item=&'a str> + 'a>

该函数需要一个&str并在运行时确定的&str上返回一些迭代器。

现在,这开始变得混乱起来,我希望有人可以提出一种更好的方法来实现这一目标。

一种可行的方法是在str::split_whitespacestr::split_ascii_whitespace周围创建包装函数,以返回盒装特征而不是它们各自的SplitWhitespaceSplitAsciiWhitespace结构。我已经使用了一个辅助宏来将来自函数调用的返回值包装在Box中

macro_rules! boxed_return {
    ($fn_new:ident, $fn:path) => {
        fn $fn_new<'a>(s: &'a str) -> Box<dyn Iterator<Item=&'a str> + 'a> {
            Box::new($fn(s))
        }
    }
}

boxed_return!(split_whitespace_wrapper, str::split_whitespace);
boxed_return!(split_ascii_whitespace_wrapper, str::split_ascii_whitespace);

然后我们可以简单地如下创建分离器函数数组

let funcs = [split_whitespace_wrapper, split_ascii_whitespace_wrapper];
相关问题