Skip general function as argument

I would like to be able to transfer the general function to another function (in this case, closure) without losing the "generality" of the transferred function. Since this is a rather confusing statement, here is an example:

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) }); } 

This will not compile because the input value is of type inferenced and is no longer generic.

Full error:

 <anon>:14:15: 14:19 error: mismatched types: expected `_`, found `_` (expected integral variable, found floating-point variable) [E0308] <anon>:14 input(10.0) ^~~~ 

Is it possible in rust?

edit:

Based on the solutions given, I used the following solutions to solve a similar problem:

 #![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) }); } 
+6
source share
2 answers

As already mentioned, unfortunately, a call is monomorphized on a call site, so you cannot pass a generic function, you can only pass a monomorphic version of a generic function.

However, you can pass a function constructor:

 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) }); } 

Builder can generate instances of input on demand.

+7
source

Very interesting question! I am pretty sure that this is impossible.

Rust shotguns work as monomorphizing functions. This means that the Rust compiler will generate machine function code for each particular type with which the function is called. With one function call, the general parameters are fixed. Therefore, since you call test exactly once in main , the general parameters are fixed for this call.

This means that the type of closure is fixed and that the input parameter of the closure also has a specific type. The compiler displays all types for us, but if we try to annotate them, we quickly notice that we are facing the same problem as the compiler:

 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 }); 

This is very similar to the use case for higher type types and general closures. Both of these features are not yet available in Rust AFAIK.

However, you can still achieve what you want using dynamic dispatch:

 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)) }); } 

Of course, this is not as good as the general version, but at least it works. Also: if you really don't need ownership in input , you can change Box<Debug> to &Debug .

+2
source

All Articles