创建从任何类型到任何其他类型

时间:2017-11-26 01:32:07

标签: generics rust

我正在尝试创建一个函数向量,这个想法是第一个函数的输出将被传递到第二个函数的输入等等。

我很难理解如何(如果可能的话)将其编码到Rust类型系统中。我尝试使用泛型但遇到了问题,因为泛型期望输入和输出对于函数向量中的每个元素总是相同的。

例如,功能一可能是i32 -> String,功能二String -> bool和功能三bool -> f64

我的尝试:

fn main() {
    let mut funcs: Vec<Box<Fn(i32) -> i32>> = Vec::new();

    funcs.push(Box::new(|a| a * 2));
    funcs.push(Box::new(|b| b * 3));

    // This won't work since it's not a Fn(i32) -> i32
    funcs.push(Box::new(|c| String::from(c)));

    // How can I create a vec of Fn(anything) -> anything where the anythings can be different for every item in the vector?
}

我开始认为Rust中唯一可行的方法是使用宏创建特定数量元素的结构。

1 个答案:

答案 0 :(得分:3)

您可以在Box中使用Any特征,但是:

  • 如果类型不匹配而不是编译时错误,您将收到运行时错误
  • 表演可能很糟糕

Playground

use std::any::Any;

pub type FnAnyToAny = Fn(Box<Any>) -> Box<Any>;

pub fn make_any_to_any<I, O, F>(f: F) -> Box<FnAnyToAny>
where
    I: 'static,
    O: 'static,
    F: Fn(I) -> O + 'static,
{
    Box::new(move |i: Box<Any>| -> Box<Any> {
        let i: Box<I> = Box::<Any + 'static>::downcast(i).expect("wrong input type");
        Box::new(f(*i))
    })
}

pub fn run_all_any<I, O>(funcs: &Vec<Box<FnAnyToAny>>, i: I) -> O
where
    I: 'static,
    O: 'static,
{
    let i: Box<Any> = Box::new(i);
    let o = funcs.iter().fold(i, |acc, f| f(acc));
    let o: Box<O> = Box::<Any + 'static>::downcast(o).expect("wrong output type");
    *o
}

fn main() {
    let mut funcs: Vec<Box<FnAnyToAny>> = Vec::new();

    funcs.push(make_any_to_any(|a: i32| a * 2));
    funcs.push(make_any_to_any(|b: i32| b * 3));

    funcs.push(make_any_to_any(|c: i32| format!("{}", c)));

    println!("{:?}", run_all_any::<i32, String>(&funcs, 4));
}

在您提到的评论中,您实际上想要处理大量输入项并在单独的线程中运行每个函数。我认为rayon箱子应该处理这个问题:

Playground

extern crate rayon;
use rayon::prelude::*;

fn main() {
    let input = vec![1, 2, 3, 4];

    // without parallel processing you'd start with:
    // let output = input.into_iter()
    let output = input
        .into_par_iter()
        .map(|a| a * 2)
        .map(|b| b * 3)
        .map(|c| format!("{}", c))
        .collect::<Vec<_>>();

    println!("{:?}", output);
}

如果你真的等了很多,例如对于网络数据(而不是实际需要CPU时间),您可能需要查看futures包,尤其是futures::stream::Stream