Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does partial application in Rust have overhead?

Tags:

closures

rust

I like using partial application, because it permits (among other things) to split a complicated function call, that is more readable.

An example of partial application:

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn main() {
    let add7 = |x| add(7, x);

    println!("{}", add7(35));
}

Is there overhead to this practice?

Here is the kind of thing I like to do (from a real code):

fn foo(n: u32, things: Vec<Things>) {
    let create_new_multiplier = |thing| ThingMultiplier::new(thing, n); // ThingMultiplier is an Iterator
    let new_things = things.clone().into_iter().flat_map(create_new_multiplier);

    things.extend(new_things);
}

This is purely visual. I do not like to imbricate too much the stuff.

like image 680
Boiethios Avatar asked Oct 03 '17 16:10

Boiethios


People also ask

Does rust support method overloading?

Rust does not support traditional overloading where the same method is defined with multiple signatures. But traits provide much of the benefit of overloading: if a method is defined generically over a trait, it can be called with any type implementing that trait. Compared to traditional overloading, this has two advantages.

What is rust used for in OS development?

Rust is high-level programming language that can easily do low-level operations. It mostly used in OS Developing because its really strong in many aspects that related to this subject like: Preventing segmentation faults. it introduce a new way in memory safety.

Why is rust so low-cost?

This mantra did not always apply to Rust, which for example used to have mandatory garbage collection. But over time Rust's ambitions have gotten ever lower-level, and zero-cost abstraction is now a core principle. The cornerstone of abstraction in Rust is traits: Traits are Rust's sole notion of interface.

Is rust a good choice for system programming?

Rust is pretty good choice for system applications because it allows you to catch most of the bugs in development, write memory and thread safe applications and even use some higher-level constructs without losing speed (which is critical factor in this cases). There’s even an operating system called Redox [2], written in Rust.


2 Answers

There should not be a performance difference between defining the closure before it's used versus defining and using it it directly. There is a type system difference — the compiler doesn't fully know how to infer types in a closure that isn't immediately called.

In code:

let create_new_multiplier = |thing| ThingMultiplier::new(thing, n);
things.clone().into_iter().flat_map(create_new_multiplier)

will be the exact same as

things.clone().into_iter().flat_map(|thing| { 
    ThingMultiplier::new(thing, n)
})

In general, there should not be a performance cost for using closures. This is what Rust means by "zero cost abstraction": the programmer could not have written it better themselves.

The compiler converts a closure into implementations of the Fn* traits on an anonymous struct. At that point, all the normal compiler optimizations kick in. Because of techniques like monomorphization, it may even be faster. This does mean that you need to do normal profiling to see if they are a bottleneck.

like image 108
Shepmaster Avatar answered Nov 15 '22 08:11

Shepmaster


In your particular example, yes, extend can get inlined as a loop, containing another loop for the flat_map which in turn just puts ThingMultiplier instances into the same stack slots holding n and thing.

But you're barking up the wrong efficiency tree here. Instead of wondering whether an allocation of a small struct holding two fields gets optimized away you should rather wonder how efficient that clone is, especially for large inputs.

like image 23
the8472 Avatar answered Nov 15 '22 10:11

the8472