Skip to content

Instantly share code, notes, and snippets.

@sigriston
Last active October 25, 2016 00:45
Show Gist options
  • Save sigriston/09fb0fa908f9319402b2d211d6e0c94a to your computer and use it in GitHub Desktop.
Save sigriston/09fb0fa908f9319402b2d211d6e0c94a to your computer and use it in GitHub Desktop.
Closures - JavaScript vs. Python
#!/usr/bin/env node
let funcs = []
for (let i = 0; i < 4; i++) {
funcs.push(x => x * i)
}
console.log(funcs[0](10)) // prints '0'
console.log(funcs[1](10)) // prints '10'
console.log(funcs[2](10)) // prints '20'
console.log(funcs[3](10)) // prints '30'
// If you're a JavaScript programmer, the results above look pretty obvious.
// However, if you're a Python programmer with no familiarity with JavaScript,
// the results might have been a little surprising.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
funcs = []
for i in range(4):
funcs.append(lambda x: x * i)
print(funcs[0](10)) # prints '30'
print(funcs[1](10)) # prints '30'
print(funcs[2](10)) # prints '30'
print(funcs[3](10)) # prints '30'
# If you're a knowledgeable Python developer, the results above are expected.
# However, if you are a JavaScript dev who mostly dabbles in Python here and
# there, you must have freaked out at this point.
// Rust, however, allows for both styles of closures, allowing you to get the
// same results as either Python or JavaScript depending on what you need.
use std::cell::Cell;
fn python_closure<'a>() {
let ic = Cell::new(0);
type PythonClosure<'a> = Box<Fn(i32) -> i32 + 'a>;
let mut funcs: Vec<PythonClosure> = Vec::new();
for i in 0..4 {
ic.set(i);
// Closures in Python have what Rust calls "borrow semantics", taking
// references to local variables in its environment.
funcs.push(Box::new(|x| x * ic.get()));
}
println!("{}", (*funcs[0])(10)); // prints "30"
println!("{}", (*funcs[1])(10)); // prints "30"
println!("{}", (*funcs[2])(10)); // prints "30"
println!("{}", (*funcs[3])(10)); // prints "30"
}
fn js_closure() {
type JSClosure = Box<Fn(i32) -> i32>;
let mut funcs: Vec<JSClosure> = Vec::new();
for i in 0..4 {
// Closures in JavaScript have what Rust calls "move semantics", where
// the closure gets a copy of values in its environment at the time the
// closure is defined.
funcs.push(Box::new(move |x| x * i));
}
println!("{}", (*funcs[0])(10)); // prints "0"
println!("{}", (*funcs[1])(10)); // prints "10"
println!("{}", (*funcs[2])(10)); // prints "20"
println!("{}", (*funcs[3])(10)); // prints "30"
}
pub fn main() {
println!("Python-style closures:");
python_closure();
println!("JavaScript-style closures:");
js_closure();
}
@NiltonVolpato
Copy link

NiltonVolpato commented Oct 24, 2016

You can also get javascript-like behavior in python doing something like:
funcs.append(lambda x, i=i: x * i)

@sigriston
Copy link
Author

@NiltonVolpato Yeah, that is a way!
In my production code though, what I really wanted to do was effectively a partial, so I ended up using just functools.partial.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment