我们如何在 Rust 中存储异构函数链?

Mig*_*ell 4 rust

我正在编写一些代码,我对延迟评估的函数链感兴趣。换句话说,它存储您想要的所有操作,并且只对它们进行一起评估。

当链中的所有函数都采用相同的类型并返回相同的类型时,这非常容易。然而,当函数链每次返回不同的类型时,我一直困惑于如何使其工作。这个简单的例子可以在下面的代码中看到:

struct FuncChain<T> {
    funcs: Vec<fn(T) -> T>
}

impl<T> FuncChain<T> {
    fn call(&self, input: T) -> T {
        self.funcs.iter().fold(input, |prev, func| func(prev))
    }
}

fn main(){
    let fc = FuncChain {
        funcs: vec![
            |x| x + 1,  
            |x| x + 2,
            |x| x * 2,
            |x| x - 2,
        ]
    };
    println!("{}", fc.call(1));
}
Run Code Online (Sandbox Code Playgroud)

(游乐场)

所以在这种情况下我们就去i32 -> i32 -> i32 -> i32 -> i32。


我想做的是一个更一般的情况,我们去A -> B -> C -> D -> E,这意味着funcs向量包含:fn(A) -> B、fn(B) -> C、fn(C) -> D和fn(D) -> E。但是如何将此类型定义分配给结构体呢?我无法创建具有异构类型的向量,即使可以,该结构的类型签名是什么?

我也许可以进行递归类型定义,其中保存FuncChain指向第一个函数对象的指针,以及链中的下一个对象:

struct FuncChain<S, T, U> {
    func: fn(S) -> T,
    next: FuncChain<T, U, ?>
}

impl<S, T, U> FuncChain<S, T, U> {
    fn call(&self, input: T) -> T {
        self.funcs.iter().fold(input, |prev, func| func(prev))
    }
}

fn main(){
    let fc = FuncChain {
        funcs: vec![
            |x| x.toString(),  
            |x| u8::fromStr(x),
            |x| x.toString(),
            |x| i32::fromStr(x),
        ]
    };
    println!("{}", fc.call(1));
}
Run Code Online (Sandbox Code Playgroud)

但是,这当然行不通,因为我不知道 的输出类型next。

如何才能做到这一点?

Mas*_*inn 6

您的问题类似于Iterator,因此可以解决相同的解决方案:指示“可调用”的特征。

该特征使您可以通过让结构将其表示为“无论做什么”来“打破”当前基于结构的系统的无限递归。

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f0d6bcc9eb8e070c1d9b6469f6a5e148

struct Chain<U, V, F> {
    prev: F,
    f: fn(U) -> V,
}

trait FuncChain<T, U> {
    fn call(&self, _: T) -> U;
    fn chain<V>(self, next: fn(U) -> V) -> Chain<U, V, Self>
    where
        Self: Sized,
    {
        Chain {
            prev: self,
            f: next,
        }
    }
}

impl<T, U> FuncChain<T, U> for fn(T) -> U {
    fn call(&self, t: T) -> U {
        self(t)
    }
}

impl<T, U, V, F> FuncChain<T, V> for Chain<U, V, F>
where
    F: FuncChain<T, U>,
{
    fn call(&self, t: T) -> V {
        (self.f)(self.prev.call(t))
    }
}

fn main() {
    let c = ((|x| x + 1) as fn(i32) -> i32)
        .chain(|x| x * 2)
        .chain(|x| x - 2);
    println!("{}", c.call(5));
}
Run Code Online (Sandbox Code Playgroud)

更好的 Rustacean 可能可以设计一种更简单的方法来实现这一目标。

如果您愿意使用 nightly,那么可能有一种方法可以使用Fn而不需要自定义特征。

天啊,从根本上来说,这只是.让你可以只用一个通用函数和一个闭包来管理,我必须检查一下。

  • 这是将函数组合作为单个函数的简单实现:https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=a24fee4afa2a13292aed0736179c21ac 它并不那么容易使用,因为你不能将它链接起来很好,并且无法访问作为闭包链内的捕获存在的函数链。 (2认同)