我希望能够将泛型函数传递给另一个函数(在本例中为闭包),而不会丢失传递函数的"泛型".由于这是一个非常复杂的陈述,这是一个例子:
use std::fmt::Debug;
fn test<F, I: Debug>(gen: F) where F: Fn(fn(I) -> I) -> I {
fn input<I: Debug>(x: I) -> I {
x
}
println!("{:?}", gen(input));
}
fn main() {
test(|input| {
input(10);
input(10.0)
});
}
Run Code Online (Sandbox Code Playgroud)
这将无法编译,因为值的input类型是推断的,不再是通用的.
完整错误:
<anon>:14:15: 14:19 error: mismatched types:
expected `_`,
found `_`
(expected integral variable,
found floating-point variable) [E0308]
<anon>:14 input(10.0)
^~~~
Run Code Online (Sandbox Code Playgroud)
生锈有可能吗?
编辑:
基于给出的解决方案,我使用以下方法来解决类似的问题:
#![feature(unboxed_closures)]
#![feature(fn_traits)]
use std::ops::Fn;
use std::ops::Add;
use std::ops::FnMut;
use std::fmt::Debug;
struct Builder;
impl Builder {
pub fn build<A: Add<B>, B: Add<A>>(&self) -> fn(A, B) -> <A as std::ops::Add<B>>::Output {
fn c<A: Add<B>, B: Add<A>>(a: A, b: B) -> <A as std::ops::Add<B>>::Output {
a + b
}
return c;
}
}
impl<A: Add<B>, B: Add<A>> Fn<(A, B)> for Builder {
extern "rust-call" fn call(&self, args: (A, B)) -> <A as std::ops::Add<B>>::Output {
let (a1, a2) = args;
self.build()(a1, a2)
}
}
impl<A: Add<B>, B: Add<A>> FnMut<(A, B)> for Builder {
extern "rust-call" fn call_mut(&mut self, args: (A, B)) -> <A as std::ops::Add<B>>::Output {
let (a1, a2) = args;
self.build()(a1, a2)
}
}
impl<A: Add<B>, B: Add<A>> FnOnce<(A, B)> for Builder {
type Output = <A as std::ops::Add<B>>::Output;
extern "rust-call" fn call_once(self, args: (A, B)) -> <A as std::ops::Add<B>>::Output {
let (a1, a2) = args;
self.build()(a1, a2)
}
}
fn test<F, I: Debug>(gen: F) where F: Fn(Builder) -> I {
let b = Builder;
println!("{:?}", gen(b));
}
fn main() {
test(|builder| {
builder(10, 10);
builder(10.1, 10.0)
});
}
Run Code Online (Sandbox Code Playgroud)
非常有趣的问题!我敢肯定它是不是有可能像。
Rust 泛型通过单态函数工作。这意味着 Rust 编译器将为调用函数的每个具体类型生成函数的机器代码。在一次函数调用中,泛型参数是固定的。所以,既然你叫test正是曾经在main,通用参数是固定的这一呼吁。
这意味着闭包类型是固定的,并且闭包的input参数也有一个具体的类型。编译器为我们推断出所有类型,但是如果我们尝试注释这些类型,我们很快就会注意到我们遇到了与编译器相同的问题:
test::<_, usize> // we can't ever spell out a closure type, therefore '_'
(|input: fn(usize) -> usize| // we can't have a generic closure right now
{
input(10); // works
input(10.0) // doesn't work
});
Run Code Online (Sandbox Code Playgroud)
这看起来很像更高级类型和泛型闭包的用例。这两个功能在 Rust 中尚不可用,AFAIK。
但是,您仍然可以通过使用动态分派来实现您想要的:
fn test<F, I: Debug>(gen: F) where F: Fn(fn(Box<Debug>) -> Box<Debug>) -> I {
fn input(x: Box<Debug>) -> Box<Debug> {
x
}
println!("{:?}", gen(input));
}
fn main() {
test(|input| {
input(Box::new(10));
input(Box::new(10.0))
});
}
Run Code Online (Sandbox Code Playgroud)
当然,这不如通用版本好,但至少它有效。另外:如果您实际上不需要在 中的所有权input,则可以更改Box<Debug>为&Debug。
如前所述,不幸的是,调用在调用站点被单态化,因此您无法传递泛型函数,您只能传递泛型函数的单态化版本.
但是,您可以传递的是函数构建器:
use std::fmt::Debug;
struct Builder;
impl Builder {
fn build<I: Debug>(&self) -> fn(I) -> I {
fn input<I: Debug>(x: I) -> I { x }
input
}
}
fn test<F, T: Debug>(gen: F)
where F: Fn(Builder) -> T
{
let builder = Builder;
println!("{:?}", gen(builder));
}
fn main() {
test(|builder| {
builder.build()(10);
builder.build()(10.0)
});
}
Run Code Online (Sandbox Code Playgroud)
在Builder能够产生的实例input上的需求.