Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compose functions in Rust?

I'm trying to write a function that composes two functions. The initial design is pretty simple: a function that takes two functions and returns a composed function which I can then compose with other functions, since Rust doesn't have rest parameters. I've run into a wall built with frustrating non-helpful compiler errors.

My compose function:

fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a> where     F: 'a + Fn(A) -> B + Sized,     G: 'a + Fn(B) -> C + Sized, {     Box::new(move |x| g(f(x))) } 

How I would like to use it:

fn main() {     let addAndMultiply = compose(|x| x * 2, |x| x + 2);     let divideAndSubtract = compose(|x| x / 2, |x| x - 2);      let finally = compose(*addAndMultiply, *divideAndSubtract);     println!("Result is {}", finally(10)); } 

The compiler doesn't like that, no matter what I try, the trait bounds are never satisfied. The error is:

error[E0277]: the size for values of type `dyn std::ops::Fn(_) -> _` cannot be known at compilation time   --> src/main.rs:13:19    | 13 |     let finally = compose(*addAndMultiply, *divideAndSubtract);    |                   ^^^^^^^ doesn't have a size known at compile-time    |    = help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn(_) -> _`    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait> note: required by `compose`   --> src/main.rs:1:1    | 1  | / fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a> 2  | | where 3  | |     F: 'a + Fn(A) -> B + Sized, 4  | |     G: 'a + Fn(B) -> C + Sized, 5  | | { 6  | |     Box::new(move |x| g(f(x))) 7  | | }    | |_^ 
like image 906
Seun LanLege Avatar asked Aug 20 '17 21:08

Seun LanLege


People also ask

What does -> mean in Rust?

It means the function never returns (usually because it unconditionally panics or otherwise ends the program, or because it contains an infinite loop that prevents a return from ever happening). The appendix describes it as: ! Always empty bottom type for diverging functions.

Where is the function in Rust?

Functions are the building blocks of readable, maintainable, and reusable code. A function is a set of statements to perform a specific task. Functions organize the program into logical blocks of code.

Does Rust need a main function?

Rust does have a way to write a main function that returns a value, however it is normally abstracted within stdlib.


2 Answers

As @ljedrz points out, to make it work you only need to reference the composed functions again:

let finally = compose(&*multiply_and_add, &*divide_and_subtract); 

(Note that in Rust, convention dictates that variable names should be in snake_case)


However, we can make this better!

Since Rust 1.26, we can use abstract return types (previously featured gated as #![feature(conservative_impl_trait)]). This can help you simplify your example greatly, as it allows you to skip the lifetimes, references, Sized constraints and Boxes:

fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C where     F: Fn(A) -> B,     G: Fn(B) -> C, {     move |x| g(f(x)) }  fn main() {     let multiply_and_add = compose(|x| x * 2, |x| x + 2);     let divide_and_subtract = compose(|x| x / 2, |x| x - 2);      let finally = compose(multiply_and_add, divide_and_subtract);     println!("Result is {}", finally(10)); } 

Finally, since you mention rest parameters, I suspect that what you actually want is to have a way to chain-compose as many functions as you want in a flexible manner. I wrote this macro for this purpose:

macro_rules! compose {     ( $last:expr ) => { $last };     ( $head:expr, $($tail:expr), +) => {         compose_two($head, compose!($($tail),+))     }; }  fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C where     F: Fn(A) -> B,     G: Fn(B) -> C, {     move |x| g(f(x)) }  fn main() {     let add = |x| x + 2;     let multiply = |x| x * 2;     let divide = |x| x / 2;     let intermediate = compose!(add, multiply, divide);      let subtract = |x| x - 2;     let finally = compose!(intermediate, subtract);      println!("Result is {}", finally(10)); } 
like image 132
Jan Hohenheim Avatar answered Sep 17 '22 13:09

Jan Hohenheim


Just add references in finally and it will work:

fn main() {     let addAndMultiply = compose(|x| x * 2, |x| x + 2);     let divideAndSubtract = compose(|x| x / 2, |x| x - 2);      let finally = compose(&*addAndMultiply, &*divideAndSubtract);     println!("Result is {}", finally(10)); } 

Dereferencing addAndMultiply or divideAndSubtract uncovers a trait object which is not Sized; it needs to either be wrapped in a Box or referenced in order for it to be passed to a function with a Sized constraint.

like image 35
ljedrz Avatar answered Sep 17 '22 13:09

ljedrz