Sitemap

Understanding Closures in Programming: What They Are and Why They’re Called “Closures”

3 min readMay 17, 2025

--

Closures are a fundamental concept in many modern programming languages like JavaScript, Python, and Rust. If you’re diving deeper into functional programming or exploring languages that support first-class functions, you’re bound to encounter closures. But what exactly is a closure, and why is it called that?

Let’s unpack this concept from first principles.

What Is a Closure?

A closure is a function bundled together with its surrounding environment — that is, the variables that were in scope when the function was defined.

Normally, functions can only access their parameters and local variables. However, with closures, functions can also reference variables from their enclosing scopes even after those scopes have exited. This is what makes closures powerful.

For example, in JavaScript:

function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2

The returned function retains access to the count variable even though makeCounter() has finished execution. That inner function is a closure.

Why Is It Called a “Closure”?

The name closure comes from the idea that the function closes over the free variables in its expression.

  • A free variable is a variable used in a function that is not a parameter or defined locally in that function.
  • When a closure is created, it “closes” the function with the environment that defines those free variables.

This terminology has roots in lambda calculus and functional language semantics. A closure is not just the function code, but also a record of the environment where the function was defined.

Implementing Closures in C

C doesn’t natively support closures, but we can simulate them using structures and function pointers.

Here’s an example of implementing a counter using a closure-like pattern in C:

#include <stdio.h>
#include <stdlib.h>

// Define a function pointer type for our closure
typedef int (*FuncPtr)(void *env);
// Define a struct that holds the environment and the function
typedef struct {
void *env;
FuncPtr func;
} Closure;
// The environment structure for our counter
typedef struct {
int count;
} CounterEnv;
// The actual function that uses the environment
int counterFunc(void *env) {
CounterEnv *counterEnv = (CounterEnv *)env;
return ++(counterEnv->count);
}
// Factory function to create a counter closure
Closure *makeCounter() {
Closure *closure = malloc(sizeof(Closure));
CounterEnv *env = malloc(sizeof(CounterEnv));
env->count = 0;
closure->env = env;
closure->func = counterFunc;
return closure;
}
// Cleanup function
void freeClosure(Closure *closure) {
free(closure->env);
free(closure);
}
// Example usage
int main() {
Closure *counter = makeCounter();
printf("%d\n", counter->func(counter->env)); // 1
printf("%d\n", counter->func(counter->env)); // 2
printf("%d\n", counter->func(counter->env)); // 3
freeClosure(counter);
return 0;
}

How This Works

  • CounterEnv holds the "closed-over" state (count).
  • counterFunc acts like the inner function, using the environment passed to it.
  • Closure bundles together a pointer to the environment and the function.
  • makeCounter() builds the closure, returning a pointer that can be reused.

This mirrors how closures work at a low level in languages that support them natively.

Conclusion

Closures let functions “remember” the context in which they were created. This makes them incredibly useful for building things like callbacks, event handlers, and encapsulated state.

The term closure reflects the mechanism of closing over free variables in a function expression, giving the function persistent access to its creation-time environment.

Even in languages like C, where closures aren’t a built-in feature, you can still implement them with a little creativity and an understanding of how functions and memory work.

--

--

No responses yet