In Rust, is it possible to return a static function from a function?

Viewed 240

Suppose I have the following type in Rust:

type UnOp = fn(u64) -> u64;

That type allows me to create different unary operations:

const add1 : UnOp = |x| x + 1;
const mul2 : UnOp = |x| x * 2;

Now, suppose that I needed add2, add3, add4 (...), for different numbers, in different places of my code. Writing all definitions would be cumbersome, so, instead, I write a generic adder function:

fn adder(add : u64) -> UnOp {
  |x| x + add
}

This would allow me to write add(2), add(3), etc., for any number:

// Prints 2 + 40
println!("result is: {}", adder(2)(40))

The problem is: adder is actually not valid Rust code, since |x| ... is a closure, not a static function. In order for adder to work as I want, I need to modify UnOp to be a closure instead:

type UnOp = Box<dyn Fn(u64) -> u64>;

fn adder(add : u64) -> UnOp {
  Box::new(move |x| x + add)
}

The problem is: what if I know every usage of adder(N) is applied to a static value?

In that case, creating dynamic closures would be wasteful, computation-wise. Not only that, Box<dyn ...> greatly complicates the code, and may even need lifetime annotations. My question is:

Is it possible to create adder, without modifying the original definition of UnOp? That is, letting UnOp be a static function, NOT a closure?

Logically, there is no reason it shouldn't be possible, as long as the arguments of adder are static, Rust should be able to just expand it at compile time, to generate each particular instance.


Edit: additional details

A great solution, proposed by @Netwave's answer, is to use Generic consts, which solves this problem for the specific example I gave. Sadly, it doesn't work in case UnOp is polymorphic, or if the constant argument is itself a function:

type UnOp<A> = fn(A) -> A;

pub fn adder<const ADD: u64>() -> UnOp<u64> {
  |x| ADD + x
}

pub fn apply<A, const op : UnOp<A>, const x : A>() -> A {
  return op(x);
}

This raises 2 errors:

- the type of const parameters must not depend on other generic parameters

- using function pointers as const generic parameters is forbidden
2 Answers

You can do it using const Generics:

type UnOp = fn(u64) -> u64;


const fn adder<const ADD: u64>(x: u64) -> u64 {
    ADD + x
}

fn main() {
    let add_1: UnOp = adder::<1>;
    println!("{}", add_1(1));
}

Playground

Is it possible to create adder, without modifying the original definition of UnOp? That is, letting UnOp be a static function, NOT a closure?

Since you don't explain why I will ignore this requirement. As I think you limit you option for nothing.

You could prefer a clever use of generic and trait:

trait UnOp {
    fn call(&self, _: u64) -> u64;
}

impl<F> UnOp for F
where
    F: Fn(u64) -> u64,
{
    fn call(&self, x: u64) -> u64 {
        self(x)
    }
}

fn adder(add: u64) -> impl UnOp {
    move |x| x + add
}
Related