我们如何在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));
}

(游乐场(

在这种情况下,我们使用i32 -> i32 -> i32 -> i32 -> i32


我想做的是一个更一般的情况,我们去A -> B -> C -> D -> E,这意味着funcs向量包含:fn(A) -> Bfn(B) -> Cfn(C) -> Dfn(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));
}

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

如何做到这一点?

你的问题类似于Iterator,因此可以用相同的解决方案来解决:一个特征表示一个"可调用";。

这种特质让你"中断";您当前基于结构的系统的无限递归,通过使结构仅将其表示为";不管它做什么";。

https://play.rust-lang.org/?version=stable&mode=调试&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));
}

一个更好的Rustacean可能会设计一种更简单的方法来实现这一点。

如果你对夜间使用很满意,那么可能有一种方法可以使用Fn,而不需要自定义特性。

见鬼,从根本上说,它只是.,所以你可能只需要一个通用函数和一个闭包就可以管理,我必须检查一下。

最新更新