Closure != Function pointer
Amit Nadiger
Polyglot(Rust??, Move, C++, C, Kotlin, Java) Blockchain, Polkadot, UTXO, Substrate, Sui, Aptos, Wasm, Proxy-wasm,AndroidTV, Dvb, STB, Linux, Cas, Engineering management.
In Rust's rich landscape of features, function pointers and closures offer powerful ways to treat functions as values. While both allow you to pass functions around, they differ significantly in their capabilities. This article dives deep into the distinctions between these concepts, exploring their definitions, creation, usage, and when to choose one over the other.
Function Pointers: Pointing to Existing Code
fn add(a: i32, b: i32) -> i32 {
a + b
}
//* From non-capturing closures (closures that don't borrow from their environment).
fn main() {
let a = 5;
let b = 10;
let add_ptr: fn(i32, i32) -> i32 = add; // function pointer
println!("add({},{}) = {}",a,b,add_ptr(a,b)); // Fun called with function pointer varaible
}
Use Cases of Function Pointers
Closures: Anonymous Functions with a Twist
fn main() {
let add_closure = |a, b| a + b;
let result = add_closure(3, 5);
println!("result = {}",result);
}
Use Cases of Closures
Both closures and function pointers can be used interchangeably:
In Rust, both closures and function pointers can be used interchangeably in situations where a function-like entity is expected. This is because closures and function pointers share some common traits:
Now, let's analyze how the operation function demonstrates this interchangeability:
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn sub(a: i32, b: i32) -> i32 {
a - b
}
fn mul(a: i32, b: i32) -> i32 {
a * b
}
fn operation(fptr: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 {
fptr(a, b)
}
fn main() {
let a = 5;
let b = 10;
// Using function pointers
println!("with function pointer add({}, {}) = {}", a, b, operation(add, a, b));
println!("with function pointer sub({}, {}) = {}", a, b, operation(sub, a, b));
println!("with function pointer mul({}, {}) = {}", a, b, operation(mul, a, b));
println!("***************************************");
// Using closures
println!("with closure add({}, {}) = {}", a, b, operation(|a, b| a + b, a, b));
println!("with closure sub({}, {}) = {}", a, b, operation(|a, b| a - b, a, b));
println!("with closure mul({}, {}) = {}", a, b, operation(|a, b| a * b, a, b));
}
领英推荐
Differences between function pointer and closure:
Things to note down w.r.t Memory:
Function Pointers:
Function pointers in Rust are essentially pointers to functions. They hold the memory address of the function they point to. When you create a function pointer, it occupies a fixed size of memory, typically the size of a pointer on the platform (e.g., 8 bytes on a 64-bit system). This memory holds the address of the function in the program's memory space.
For example, if we have a function pointer add_ptr pointing to the add function:
let add_ptr: fn(i32, i32) -> i32 = add;
at the memory level, add_ptr would store the memory address of the add function.
Closures:
Memory Allocation in Closures in Rust is a bit more complex.
Closures in Rust can be handled in two ways depending on whether they capture variables from their environment:
For example, if we have a closure capturing variables x and y:
let closure = |x, y| x + y;
At the memory level, closure would consist of a struct containing a function pointer to the code implementing the closure's behavior and space for the captured variables x and y. This struct would be allocated on the stack at the point where the closure is created.
Key Points:
In essence, function pointers and closures provide different ways to achieve a similar goal - passing functions as arguments. However, their memory management strategies differ based on whether they capture variables.