Pass Generic Function as argument - rust

I would like to be able to pass a generic function to another function (in this case a closure), without losing the "genericness" of the passed function. Since that's a pretty convoluted statement, here's 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 value of input is type inferenced and 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 such a thing possible in rust?
edit:
Based on the solutions given, I've used the following 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)
});
}

As has been mentioned, unfortunately the call is monomorphized at the call site, so you cannot pass a generic function, you can only pass a monomorphized version of the generic function.
What you can pass, however, is a function builder:
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)
});
}
The Builder is able to generate instances of input on demand.

Very interesting question! I'm pretty sure it's not possible like that.
Rust generics work by monomorphizing functions. This means that the Rust compiler will generate the machine code of the function for every concrete type the function is invoked with. Within one call of a function, the generic parameters are fixed. So since you call test exactly once in main, the generic parameters are fixed for that call.
This implies that the closure type is fixed and that the input parameter of the closure has a concrete type, too. The compiler deduces all the types for us, but if we would try to annotate these, we quickly notice that we run into 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 looks a lot like a use case for higher kinded types and generic closures. Both of those features are not available in Rust yet, AFAIK.
However, you can still achieve what you want by 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 nice as the generic version, but at least it works. Also: if you don't actually need ownership in input, you can change Box<Debug> to &Debug.

Related

Why am I able to write a function type in the type parameter of a struct?

If I understand correctly, in Rust every closure type has a unique type that cannot be written out. I also thought this applied to functions, however, I'm able to do the following, in which I explicitly write the type parameter in the return types of get_struct_1 and get_struct_2:
struct FooStruct<F>
where F: Fn(i32) -> i32
{
f: F,
}
fn foo(x: i32) -> i32 {
2*x
}
fn bar(x: i32) -> i32 {
-1*x
}
fn get_struct_1() -> FooStruct<fn(i32) -> i32>
{
FooStruct { f: foo }
}
fn get_struct_2() -> FooStruct<fn(i32) -> i32>
{
FooStruct { f: bar }
}
// This does not work - the trait has to be boxed
//fn get_struct_3() -> FooStruct<Fn(i32) -> i32>
//{
// FooStruct { f: |x| 10*x }
//}
fn main() {
let mut x = get_struct_1();
// Why does this work - do bar and foo have the same type?
x = get_struct_2();
// Why does this work - doesn't a closure have its own unique, unwriteable type?
x = FooStruct { f: |x| 10*x };
let mut y = FooStruct { f: |x| 10*x };
// Does not work - no two closures have the same type.
//y = FooStruct { f: |x| 10*x };
// Does not work - even though the 'other way around' worked with x.
// But _does_ work if I type-annotate y with FooStruct<fn(i32) -> i32>
//y = get_struct_1();
}
I thought Rust was monomorphic in the way it handles type parameters. So if I do this
struct FooStruct {
f: Box<dyn Fn(i32) -> i32>
}
the program will dynamically determine which f to run at runtime, but the FooStruct<F> version avoids the dynamic dispatch.
This example seems to disagree with that. If the x = get_struct_2(); line were inside an if statement, the compiler would not be able to determine whether x was holding a wrapped version of the function foo or bar.
Closures (and functions for that matter) do have unique, unwriteable types. However, they can also be cast (and implicitly* too) to function pointers when they don't capture any variables, which yours don't. It's essentially the reason why this works:
fn main() {
// closure is inferred to be a function pointer
let mut f: fn() -> i32 = || 5;
// assigning a different function pointer
f = || 6;
}
But this doesn't:
fn main() {
// closure is inferred to be a unique closure type
let mut f = || 5;
// uh oh! different closure type, errors
f = || 6;
}
* it's not so much an implicit cast as implicit type inference

What is the correct syntax to return a function in Rust?

What is the correct syntax to return a function in Rust?
The following code does not compile, thanks.
fn identity<T>(a: T) -> T {
return a;
};
fn right<T>(a: T) -> Fn {
return identity;
};
Here (playground) is a minimal example:
fn identity<T>(a: T) -> T {
return a;
}
fn right<T>(_a: T) -> impl Fn(T) -> T {
return identity;
}
fn main() {
println!("{}", right(0)(42))
}
You need to:
Specify the input argument and output types within the signature, i.e. Fn(T) -> T.
Specify that right's return type implements the trait Fn(T) -> T.
Alternatively, you could also have written the function pointer fn(T) -> T as the return type. Since this is not a trait, you would not need the impl keyword:
fn right<T>(_a: T) -> fn(T) -> T {
return identity;
}
Only fn items and non-capturing closures can be coerced to function pointers, so, while simpler, this will not work in all cases.
This is the best I came up with:
fn identity<T>(a: T) -> T {
return a;
}
fn right<T>() -> &'static dyn Fn(T)->T {
return &identity::<T>;
}
fn main() {
println!("{}", right()(3.1415));
}
playground <-- test it by yourself.
Explanation
&'static dyn Fn(T)->T means that it returns a reference to some object that satisfy the Fn(T)->T trait (because yes Fn(T)->T is indeed a trait, not a type).
Self answer after two answers from others:
I came up with this:
fn identity<T>(a: T) -> T {
a
};
fn right<T, U>(_a: T) -> fn(U) -> U {
identity
};
The return type of right does not have to be identical to input of T of identity.

How to use traits for function overloading in Rust? [duplicate]

I am modeling an API where method overloading would be a good fit. My naïve attempt failed:
// fn attempt_1(_x: i32) {}
// fn attempt_1(_x: f32) {}
// Error: duplicate definition of value `attempt_1`
I then added an enum and worked through to:
enum IntOrFloat {
Int(i32),
Float(f32),
}
fn attempt_2(_x: IntOrFloat) {}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
// Can't pass the value directly
// attempt_2(i);
// attempt_2(f);
// Error: mismatched types: expected enum `IntOrFloat`
attempt_2(IntOrFloat::Int(i));
attempt_2(IntOrFloat::Float(f));
// Ugly that the caller has to explicitly wrap the parameter
}
Doing some quick searches, I've found some references that talk about overloading, and all of them seem to end in "we aren't going to allow this, but give traits a try". So I tried:
enum IntOrFloat {
Int(i32),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for i32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Int(*self)
}
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Float(*self)
}
}
fn attempt_3(_x: &dyn IntOrFloatTrait) {}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
attempt_3(&i);
attempt_3(&f);
// Better, but the caller still has to explicitly take the reference
}
Is this the closest I can get to method overloading? Is there a cleaner way?
Yes, there is, and you almost got it already. Traits are the way to go, but you don't need trait objects, use generics:
#[derive(Debug)]
enum IntOrFloat {
Int(i32),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for i32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Int(*self)
}
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Float(*self)
}
}
fn attempt_4<T: IntOrFloatTrait>(x: T) {
let v = x.to_int_or_float();
println!("{:?}", v);
}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
attempt_4(i);
attempt_4(f);
}
See it working here.
Here's another way that drops the enum. It's an iteration on Vladimir's answer.
trait Tr {
fn go(&self) -> ();
}
impl Tr for i32 {
fn go(&self) {
println!("i32")
}
}
impl Tr for f32 {
fn go(&self) {
println!("f32")
}
}
fn attempt_1<T: Tr>(t: T) {
t.go()
}
fn main() {
attempt_1(1 as i32);
attempt_1(1 as f32);
}
Function Overloading is Possible!!! (well, sorta...)
This Rust Playground example has more a more detailed example, and shows usage of a struct variant, which may be better for documentation on the parameters.
For more serious flexible overloading where you want to have sets of any number of parameters of any sort of type, you can take advantage of the From<T> trait for conversion of a tuple to enum variants, and have a generic function that converts tuples passed into it to the enum type.
So code like this is possible:
fn main() {
let f = Foo { };
f.do_something(3.14); // One f32.
f.do_something((1, 2)); // Two i32's...
f.do_something(("Yay!", 42, 3.14)); // A str, i32, and f64 !!
}
First, define the different sets of parameter combinations as an enum:
// The variants should consist of unambiguous sets of types.
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
Now, the conversion code; a macro can be written to do the tedious From<T> implementations, but here's what it could produce:
impl From<(i32, i32)> for FooParam {
fn from(p: (i32, i32)) -> Self {
FooParam::Bar(p.0, p.1)
}
}
impl From<f32> for FooParam {
fn from(p: f32) -> Self {
FooParam::Baz(p)
}
}
impl From<(&'static str, i32, f64)> for FooParam {
fn from(p: (&'static str, i32, f64)) -> Self {
FooParam::Qux(p.0, p.1, p.2)
}
}
And then finally, implement the struct with generic method:
struct Foo {}
impl Foo {
fn do_something<T: Into<FooParam>>(&self, t: T) {
use FooParam::*;
let fp = t.into();
match fp {
Bar(a, b) => print!("Bar: {:?}, {:?}\n", a, b),
Baz(a) => print!("Baz: {:?}\n", a),
Qux(a, b, c) => {
print!("Qux: {:?}, {:?}, {:?}\n", a, b, c)
}
}
}
}
Note: The trait bound on T needs to be specified.
Also, the variants need to be composed of combinations of types that the compiler wouldn't find ambiguous - which is an expectation for overloaded methods in other languages as well (Java/C++).
This approach has possibilities... it would be awesome if there's a decorator available - or one were written that did the From<T> implementations automatically when applied to an enum. Something like this:
// THIS DOESN'T EXIST - so don't expect the following to work.
// This is just an example of a macro that could be written to
// help in using the above approach to function overloading.
#[derive(ParameterOverloads)]
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
// If this were written, it could eliminate the tedious
// implementations of From<...>.
The Builder
Another approach that addresses the case where you have multiple optional parameters to an action or configuration is the builder pattern. The examples below deviate somewhat from the recommendations in the link. Typically, there's a separate builder class/struct which finalizes the configuration and returns the configured object when a final method is invoked.
One of the most relevant situations this can apply to is where you want a constructor that takes a variable number of optional arguments - since Rust doesn't have built-in overloading, we can't have multiple versions of ___::new(). But we can get a similar effect using a chain of methods that return self. Playground link.
fn main() {
// Create.
let mut bb = BattleBot::new("Berzerker".into());
// Configure.
bb.flame_thrower(true)
.locomotion(TractorTreads)
.power_source(Uranium);
println!("{:#?}", bb);
}
Each of the configuration methods has a signature similar to:
fn power_source(&mut self, ps: PowerSource) -> &mut Self {
self.power_source = ps;
self
}
These methods could also be written to consume self and return non-reference copies or clones of self.
This approach can also be applied to actions. For instance, we could have a Command object that can be tuned with chained methods, which then performs the command when .exec() is invoked.
Applying this same idea to an "overloaded" method that we want to take a variable number of parameters, we modify our expectations a bit and have the method take an object that can be configured with the builder pattern.
let mut params = DrawParams::new();
graphics.draw_obj(params.model_path("./planes/X15.m3d")
.skin("./skins/x15.sk")
.location(23.64, 77.43, 88.89)
.rotate_x(25.03)
.effect(MotionBlur));
Alternatively, we could decide on having a GraphicsObject struct that has several config tuning methods, then performs the drawing when .draw() is invoked.

How to generalize a function over two types with similar fields in different positions?

I have a function algo which works with a type S1, I also have
a type S2 which contains all of the fields of S1 plus some additional ones.
How should I modify algo to also accept S2 as input without
creating a temporary variable with type S1 and data from S2?
struct Moo1 {
f1: String,
f2: i32,
}
struct Moo2 {
f1: String,
f2: i32,
other_fields: f32,
}
struct S1 {
x: i32,
v: Vec<Moo1>,
}
struct S2 {
x: i32,
v: Vec<Moo2>,
}
//before fn algo(s: &S1)
fn algo<???>(???) {
//work with x and v (only with f1 and f2)
}
Where I'm stuck
Let's assume algo has this implementation (my real application has another implementation):
fn algo(s: &S1) {
println!("s.x: {}", s.x);
for y in &s.v {
println!("{} {}", y.f1, y.f2);
}
}
To access the field in Moo1 and Moo2 I introduce trait AsMoo, and to access x field and v I introduce trait AsS:
trait AsMoo {
fn f1(&self) -> &str;
fn f2(&self) -> i32;
}
trait AsS {
fn x(&self) -> i32;
// fn v(&self) -> ???;
}
fn algo<S: AsS>(s: &AsS) {
println!("s.x: {}", s.x());
}
I'm stuck at the implementation of the AsS::v method. I do not allocate memory to use my algo, but I need a Vec<&AsMoo> in some way.
Maybe I need to return some kind of Iterator<&AsMoo>, but have no idea how to do it and that looks complex for this problem.
Maybe I should use macros instead?
Any problem in computer science can be solved by adding another layer of indirection; at the exception of having too many such layers, of course.
Therefore, you are correct that you miss a S trait to generalize S1 and S2. In S, you can use a feature called associated type:
trait Moo {
fn f1(&self) -> &str;
fn f2(&self) -> i32;
}
trait S {
type Mooer: Moo;
fn x(&self) -> i32;
fn v(&self) -> &[Self::Mooer];
}
The bit type Mooer: Moo; says: I don't quite know what the exact type Mooer will end up being, but it'll implement the Moo trait.
This lets you write:
impl S for S1 {
type Mooer = Moo1;
fn x(&self) -> i32 { self.x }
fn v(&self) -> &[Self::Mooer] { &self.v }
}
impl S for S2 {
type Mooer = Moo2;
fn x(&self) -> i32 { self.x }
fn v(&self) -> &[Self::Mooer] { &self.v }
}
fn algo<T: S>(s: &T) {
println!("s.x: {}", s.x());
for y in s.v() {
println!("{} {}", y.f1(), y.f2());
}
}
And your generic algo knows that whatever type Mooer ends up being, it conforms to the Moo trait so the interface of Moo is available.

How to implement the Y combinator in Rust? [duplicate]

I've just started Rust tutorial and ended with such code using recursion
extern crate rand;
use std::io;
use rand::Rng;
use std::cmp::Ordering;
use std::str::FromStr;
use std::fmt::{Display, Debug};
fn try_guess<T: Ord>(guess: T, actual: T) -> bool {
match guess.cmp(&actual) {
Ordering::Less => {
println!("Too small");
false
}
Ordering::Greater => {
println!("Too big");
false
}
Ordering::Equal => {
println!("You win!");
true
}
}
}
fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T)
where <T as FromStr>::Err: Debug
{
println!("PLease input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess_int: T = guess.trim()
.parse()
.expect("Should enter integer number");
println!("You guessed {} !", guess_int);
if !try_guess(guess_int, actual) {
guess_loop(actual)
}
}
fn main() {
println!("Guess the number!!!");
let secret_number = rand::thread_rng().gen_range(1, 51);
guess_loop(secret_number);
}
I was hoping to factor-out the recursion from the guess_loop function and introduced a fix point operator:
fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T, recur: fn(T) -> ()) -> ()
where <T as FromStr>::Err: Debug
{
println!("PLease input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess_int: T = guess.trim()
.parse()
.expect("Should enter integer number");
println!("You guessed {} !", guess_int);
if !try_guess(guess_int, actual) {
recur(actual)
}
}
fn fix<T, R>(func: fn(T, fn(T) -> R) -> R) -> fn(T) -> R {
fn fixed(val: T) -> R {
func(val, fixed)
}
fixed
}
fn main() {
println!("Guess the number!!!");
let secret_number = rand::thread_rng().gen_range(1, 51);
fix(guess_loop)(secret_number);
}
but this led to numerous errors, such as
error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
--> src/main.rs:49:19
|
49 | fn fixed(val: T) -> R {
| ^ use of type variable from outer function
error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
--> src/main.rs:49:25
|
49 | fn fixed(val: T) -> R {
| ^ use of type variable from outer function
error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } closure form instead
--> src/main.rs:50:9
|
50 | func(val, fixed)
| ^^^^
My next attempt was changing guess_loop's definition to
fn guess_loop<T: Ord + FromStr + Display + Copy, F>(actual: T, recur: F) -> ()
where <T as FromStr>::Err: Debug,
F: Fn(T) -> ()
{ ... }
and redefine fix as
fn fix<T, R, F>(func: fn(T, F) -> R) -> F
where F: Fn(T) -> R
{
let fixed = |val: T| func(val, fix(func));
fixed
}
this led to
error[E0308]: mismatched types
--> src/main.rs:53:5
|
53 | fixed
| ^^^^^ expected type parameter, found closure
|
= note: expected type `F`
= note: found type `[closure#src/main.rs:52:17: 52:46 func:_]`
error: the type of this value must be known in this context
--> src/main.rs:61:5
|
61 | fix(guess_loop)(secret_number);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How can I write a similar fix function?
Firstly, variable names don't exist until after they're initialised. You can't have fixed refer to itself like that.
Secondly, you can't return closures by-value from a function, period. Generic parameters are chosen by the caller, and the caller has no idea what the type of a closure inside the function is going to be.
I'm not claiming that what follows is the best way of doing this, but it was the simplest I was able to come up with that type-checks.
fn guess_loop<T>(actual: T, recur: &Fn(T)) -> ()
where T: Ord + FromStr + Display + Copy,
<T as FromStr>::Err: Debug
{
// ...
}
fn fix<T, R, F>(func: F) -> Box<Fn(T) -> R>
where T: 'static,
R: 'static,
F: Fn(T, &Fn(T) -> R) -> R + 'static
{
use std::cell::RefCell;
use std::rc::Rc;
let fixed = Rc::new(RefCell::new(None));
let fixed_fn = {
let fixed = fixed.clone();
move |val: T| -> R {
let fixed_ref = fixed.borrow();
let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
func(val, &**fixed_ref)
}
};
*fixed.borrow_mut() = Some(Box::new(fixed_fn));
Box::new(move |val: T| -> R {
let fixed_ref = fixed.borrow();
let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
fixed_ref(val)
})
}
In order for fixed_fn to refer to itself, we have to create something for it to read from before it exists. Unfortunately, this means having a cycle, and Rust hates cycles. So, we do this by constructing a reference-counted RefCell<Option<_>> that starts with None, and which will be mutated later to contain the fixed-point closure.
Secondly, we can't use this handle as a callable, so we have to explicitly pull a pointer to the closure out so that we can pass it to func.
Third, the compiler doesn't seem to be able to infer the type of fixed correctly. I was hoping it would be able to work out that it is Rc<RefCell<Option<{closure}>>>, but it refused to do so. As a result, we have to resort to storing a Box<Fn(T) -> R>, since we can't name the type of the closure explicitly.
Finally, we have to construct a new closure that takes a second handle to fixed, unpacks it, and calls it. Again, we can't use fixed as a callable directly. We also can't re-use the closure inside fixed, because to do that we'd have to put that inside its own Rc and at that point, things are starting to get crazy.
... more crazy.
Finally, we have to return this second closure in a Box because, as I said before, we can't return closures by value because we can't name their types in the signature.
*deep breath*
If someone has a simpler solution, I'd love to see it. :P
This is an answer to my own question about implementing the Y combinator which is a subset of this question. In pure lambda expression, a version of the Y combinator looks like
λf.(λw.w w)(λw.f (w w))
The solution in Rosetta Code is too complicated and used Box to allocate memory in the heap. I want to simplify this.
First, let's implement the type Mu<T> as a trait instead.
trait Mu<T> {
fn unroll(&self, &Mu<T>) -> T;
}
Note that we need this trait to be object safe, which means we cannot ask for Self in any of its definition so the second parameter is typed &Mu<T> and it is a trait object.
Now we can write a generic trait implementation:
impl<T, F: Fn(&Mu<T>) -> T> Mu<T> for F {
fn unroll(&self, o: &Mu<T>) -> T {
self(o)
}
}
With this, we can now write the y combinator as the following:
fn y<T, F: Fn(T) -> T>(f: &F) -> T {
(&|w: &Mu<T>| w.unroll(w))(&|w: &Mu<T>| f(w.unroll(w)))
}
The above compiles in the Rust playground without enabling any features and using only the stable channel so this is a pretty good answer to my question.
However, the above would not work in practice because Rust is call-by-value but the code above is the call-by-name Y combinator.
The call-by-value solution
To work with the stable channel without requiring any features, we cannot return closures (which requires impl Trait). Instead, I came up with making another Mu2 type that takes two type parameters:
trait Mu2<T, R> {
fn unroll(&self, &Mu2<T, R>, t: T) -> R;
}
As above, let's implement this new trait.
impl<T, R, F> Mu2<T, R> for F
where
F: Fn(&Mu2<T, R>, T) -> R,
{
fn unroll(&self, o: &Mu2<T, R>, t: T) -> R {
self(o, t)
}
}
The new Y combinator:
fn y<T, R, F>(f: &F, t: T) -> R
where
F: Fn(&Fn(T) -> R, T) -> R,
{
(&|w: &Mu2<T, R>, t| w.unroll(w, t))((&|w: &Mu2<T, R>, t| f(&|t| w.unroll(w, t), t)), t)
}
Now it is time to test our new facility.
fn main() {
let fac = &|f: &Fn(i32) -> i32, i| if i > 0 { i * f(i - 1) } else { 1 };
println!("{}", y(fac, 10))
}
Results in:
3628800
All done!
You can see that the y function has a slightly different signature than the questioner's fix, but it shouldn't matter.
The direct recurring version
The same technology to avoid returning a closure can be used for the normal direct recurring version as well:
fn fix<T, R, F>(f: &F, t: T) -> R
where
F: Fn(&Fn(T) -> R, T) -> R,
{
f(&|t| fix(f, t), t)
}
fn fib(i: i32) -> i32 {
let fn_ = &|f:&Fn(i32) -> i32, x| if x < 2 { x } else { f(x-1) + f(x-2) };
fix(fn_, i)
}
Basically, whenever you need to return a closure from a function, you can add the closure's parameter to the function, and change the return type to the closure's return type. Later on when you need a real closure, just create the closure by partial evaluating that function.
Further discussions
Compare to other languages, in Rust there is a big difference: the function given to find fix point must not have any internal states. In Rust this is a requirement that the F type parameter of y must be Fn, not FnMut or FnOnce.
For example, we cannot implement a fix_mut that would be used like
fn fib1(i: u32) -> u32 {
let mut i0 = 1;
let mut i1 = 1;
let fn_ = &mut |f:&Fn(u32) -> u32, x|
match x {
0 => i0,
1 => i1,
_ => {
let i2 = i0;
i0 = i1;
i1 = i1 + i2;
f(x)
}
};
fix_mut(fn_, i)
}
without unsafe code whilst this version, if it works, performs much better (O(N)) than the version given above (O(2^N)).
This is because you can only have one &mut of one object at a single time. But the idea of Y combinator, or even the fix point function, requires capturing/passing the function at the same time when calling it, that's two references and you can't just mark any of them immutable without marking another so.
On the other hand, I was wonder if we could do something that other languages usually not able to but Rust seems to be able. I was thinking restricting the first argument type of F from Fn to FnOnce (as y function will provide the implementation, change to FnMut does not make sense, we know it will not have states, but change to FnOnce means we want it to be used only once), Rust would not allow at the moment as we cannot pass unsized object by value.
So basically, this implementation is the most flexible solution we could think of.
By the way, the work around of the immutable restriction is to use pseudo-mutation:
fn fib(i: u32) -> u32 {
let fn_ = &|f:&Fn((u32,u32,u32)) -> u32, (x,i,j)|
match x {
0 => i,
1 => j,
_ => {
f((x-1,j,i+j))
}
};
fix(&fn_, (i,1,1))
}
Starting at where you left off:
fn fix<T, R, F>(func: fn(T, F) -> R) -> F
where F: Fn(T) -> R
{
|val: T| func(val, fix(func))
}
The returned object has an unnameable closure type. Using a generic type won’t help here, since the type of the closure is decided by the callee, not the caller. Here’s where impl traits come in handy:
fn fix<T, R, F>(func: fn(T, F) -> R) -> impl Fn(T) -> R
where F: Fn(T) -> R
{
|val: T| func(val, fix(func))
}
We can’t pass fix(func) to func because it expects a nameable type for F. We’ll have to settle for a trait object instead:
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
|val: T| func(val, &fix(func))
}
Now it’s time to fight the lifetime checker. The compiler complains:
only named lifetimes are allowed in `impl Trait`, but `` was found in the type `…`
This is a somewhat cryptic message. Since impl traits are always 'static by default, this is a roundabout way of saying: “the closure does not live long enough for 'static”. To get the real error message, we append + 'static to the impl Fn(T) -> R and recompile:
closure may outlive the current function, but it borrows `func`, which is owned by the current function
So that was the real problem. It is borrowing func. We don’t need to borrow func because fn is Copy, so we can duplicate it as much as we want. Let’s prepend the closure with move and get rid of the + 'static from earlier:
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
move |val: T| func(val, &fix(func))
}
And voila, it works! Well, almost … you’ll have to edit guess_loop and change fn(T) -> () to &Fn(T) -> (). I’m actually quite amazed that this solution doesn’t require any allocations.
If you can’t use impl traits, you can instead write:
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> Box<Fn(T) -> R>
where T: 'static,
R: 'static
{
Box::new(move |val: T| func(val, fix(func).as_ref()))
}
which is unfortunately not allocation-free.
Also, we can generalize the result a bit to allow arbitrary closures and lifetimes:
fn fix<'a, T, R, F>(func: F) -> impl 'a + Fn(T) -> R
where F: 'a + Fn(T, &Fn(T) -> R) -> R + Copy
{
move |val: T| func(val, &fix(func))
}
In the process of figuring out a solution for your problem, I ended up writing a simpler version of fix, which actually ended up guide me towards a solution to your fix function:
type Lazy<'a, T> = Box<FnBox() -> T + 'a>;
// fix: (Lazy<T> -> T) -> T
fn fix<'a, T, F>(f: F) -> T
where F: Fn(Lazy<'a, T>) -> T + Copy + 'a
{
f(Box::new(move || fix(f)))
}
Here’s a demonstration of how this fix function could be used to calculate the factorial:
fn factorial(n: u64) -> u64 {
// f: Lazy<u64 -> u64> -> u64 -> u64
fn f(fac: Lazy<'static, Box<FnBox(u64) -> u64>>) -> Box<FnBox(u64) -> u64> {
Box::new(move |n| {
if n == 0 {
1
} else {
n * fac()(n - 1)
}
})
}
fix(f)(n)
}
This can be done at zero runtime cost if you're willing to use unstable features (i.e. a nightly compiler) and willing to... obfuscate your code slightly.
First, we need to turn the result of fix into a named struct. This struct needs to implement Fn, so we'll implement it manually (this is an unstable feature).
#![feature(fn_traits)]
#![feature(unboxed_closures)]
extern crate rand;
use rand::Rng;
use std::cmp::Ordering;
fn try_guess<T: Ord>(guess: T, actual: T) -> bool {
match guess.cmp(&actual) {
Ordering::Less => {
println!("Too small");
false
}
Ordering::Greater => {
println!("Too big");
false
}
Ordering::Equal => {
println!("You win!");
true
}
}
}
struct Fix<F>
where F: Fn(i32, &Fix<F>)
{
func: F,
}
impl<F> FnOnce<(i32,)> for Fix<F>
where F: Fn(i32, &Fix<F>)
{
type Output = ();
extern "rust-call" fn call_once(self, args: (i32,)) -> Self::Output {
self.call(args)
}
}
impl<F> FnMut<(i32,)> for Fix<F>
where F: Fn(i32, &Fix<F>)
{
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> Self::Output {
self.call(args)
}
}
impl<F> Fn<(i32,)> for Fix<F>
where F: Fn(i32, &Fix<F>)
{
extern "rust-call" fn call(&self, (val,): (i32,)) -> Self::Output {
(self.func)(val, self);
}
}
fn fix<F>(func: F) -> Fix<F>
where F: Fn(i32, &Fix<F>)
{
Fix { func: func }
}
fn guess_loop<F>(actual: i32, recur: &F)
where F: Fn(i32)
{
let guess_int = rand::thread_rng().gen_range(1, 51);
if guess_int != actual {
recur(actual)
}
}
fn main() {
let secret_number = rand::thread_rng().gen_range(1, 51);
fix(guess_loop)(secret_number);
}
However, we're not done yet. This fails to compile with the following error:
error[E0281]: type mismatch: the type `fn(i32, &_) {guess_loop::<_>}` implements the trait `for<'r> std::ops::Fn<(i32, &'r _)>`, but the trait `for<'r> std::ops::Fn<(i32, &'r Fix<fn(i32, &_) {guess_loop::<_>}>)>` is required (cyclic type of infinite size)
--> src/main.rs:77:5
|
77 | fix(guess_loop)(secret_number);
| ^^^
|
= note: required by `fix`
Note: In case you're not aware, in Rust, each function has its own, zero-sized type. If a function is generic, then each instantiation of that function will have its own type as well. For example, the type of guess_loop::<X> will be reported by the compiler as fn(i32, &X) {guess_loop::<X>} (as you can see in the error message above, except with underscores where the concrete type hasn't been resolved yet). That type can be coerced to a function pointer type implicitly in some contexts or explicitly with a cast (as).
The problem is that, in the expression fix(guess_loop), the compiler needs to instantiate guess_loop, which is a generic function, and it looks like the compiler isn't able to figure out the proper type to instantiate it with. In fact, the type we would like to set for type parameter F references the type of guess_loop. If we were to write it out in the style reported by the compiler, the type would look like fn(i32, &Fix<X>) {guess_loop::<Fix<&X>>}, where X is replaced by the type itself (you can see now where the "cyclic type of infinite size" comes from).
We can solve this by replacing the guess_loop function by a non-generic struct (we'll call it GuessLoop) that implements Fn by referring to itself. (You can't do this with a normal function because you can't name a function's type.)
struct GuessLoop;
impl<'a> FnOnce<(i32, &'a Fix<GuessLoop>)> for GuessLoop {
type Output = ();
extern "rust-call" fn call_once(self, args: (i32, &Fix<GuessLoop>)) -> Self::Output {
self.call(args)
}
}
impl<'a> FnMut<(i32, &'a Fix<GuessLoop>)> for GuessLoop {
extern "rust-call" fn call_mut(&mut self, args: (i32, &Fix<GuessLoop>)) -> Self::Output {
self.call(args)
}
}
impl<'a> Fn<(i32, &'a Fix<GuessLoop>)> for GuessLoop {
extern "rust-call" fn call(&self, (actual, recur): (i32, &Fix<GuessLoop>)) -> Self::Output {
let guess_int = rand::thread_rng().gen_range(1, 51);
if !try_guess(guess_int, actual) {
recur(actual)
}
}
}
fn main() {
let secret_number = rand::thread_rng().gen_range(1, 51);
fix(GuessLoop)(secret_number);
}
Notice that GuessLoop's implementation of Fn is no longer generic on the type of the recur parameter. What if we tried to make the implementation of Fn generic (while still leaving the struct itself non-generic, to avoid cyclic types)?
struct GuessLoop;
impl<'a, F> FnOnce<(i32, &'a F)> for GuessLoop
where F: Fn(i32),
{
type Output = ();
extern "rust-call" fn call_once(self, args: (i32, &'a F)) -> Self::Output {
self.call(args)
}
}
impl<'a, F> FnMut<(i32, &'a F)> for GuessLoop
where F: Fn(i32),
{
extern "rust-call" fn call_mut(&mut self, args: (i32, &'a F)) -> Self::Output {
self.call(args)
}
}
impl<'a, F> Fn<(i32, &'a F)> for GuessLoop
where F: Fn(i32),
{
extern "rust-call" fn call(&self, (actual, recur): (i32, &'a F)) -> Self::Output {
let guess_int = rand::thread_rng().gen_range(1, 51);
if !try_guess(guess_int, actual) {
recur(actual)
}
}
}
Unfortunately, this fails to compile with the following error:
error[E0275]: overflow evaluating the requirement `<Fix<GuessLoop> as std::ops::FnOnce<(i32,)>>::Output == ()`
--> src/main.rs:99:5
|
99 | fix(GuessLoop)(secret_number);
| ^^^
|
= note: required because of the requirements on the impl of `for<'r> std::ops::Fn<(i32, &'r Fix<GuessLoop>)>` for `GuessLoop`
= note: required by `fix`
Essentially, the compiler is unable to verify that Fix<GuessLoop> implements Fn(i32), because in order to do that, it needs to verify that GuessLoop implements Fn(i32, &Fix<GuessLoop>), but that is only true if Fix<GuessLoop> implements Fn(i32) (because that impl is conditional), which is only true if GuessLoop implements Fn(i32, &Fix<GuessLoop>) (because that impl is conditional too), which... you get the idea. In order words, the two implementations of Fn here are dependent on each other, and the compiler is unable to resolve that.

Resources