> The IIFE/outer closure here is equivalent to the capture happening in rust. So this is more equivalent to the rust examples than your python example.

Wait, I don't follow. Rewriting your example to only use one lambda for clarity, we have:

def mystery_closure_one_lambda():
x = 1
def capture(v):
return lambda: v
closure = capture(x)
x = 2
return x * closure()

So notice the lambda (i.e. what we are assigning to the variable 'closure') is now capturing v, not x, which is why it doesn't see the change we make to x, i.e., why it returns 2 instead of 4.

But this is not equivalent to the rust code! There is no v at all in rust. We are capturing x! (It's slightly obscured by the fact that we to use an unsafe ptr to defeat the borrow checker, but we are still capturing x.)

So I do not think mystery_closure is equivalent to either of the rust mystery_a or mystery_b above; it is in fact equivalent to this:

fn mystery_closure() -> i32 {
let mut x = 1;
let closure = (|v| move || v)(x);
x = 2;
x * closure()
}

Which also returns 2, just like the python code. (It's also a direct translation of the python code!)

> Let's start in python, since python doesn't allow multiline lambdas, we have to swap to using an inner function, which is fine, this makes the structure a bit clearer in python.

Careful! -- your de-lamba-fication accidentally changed the semantics. If we just de-lambda-fy, we get:

def mystery_int():
x = 1
def closure():
def inner(v):
return v
return inner(x)
x = 2
return x + closure()

Which returns 4, showing it's defnly not equivalent. The correct de-lambda-ficiation is:

def mystery_closure_no_lambas():
x = 1
def capture(v):
def inner():
return v
return inner
closure = capture(x)
x = 2
return x * closure()

(which as a sanity check, returns 2, as it should).

Bringing in mutable reference data types like vec is I think not really relevent to what's at play here.

In both rust and python, the non-reference types mut i32 (rust) and int (python) are mutable. In rust you can pass a mutable reference to an i32, and in python you can't, but so what; that's not really relevent.

DIGRESSION:

Just for funsies, you actually can achieve what are essentially mutable references in python3 (you could also do this in py2 if you wanted to get nasty with locals()):

# a mutable reference to a local variable
class Ref:
def __init__(self, getfn, setfn):
self.getfn, self.setfn = getfn, setfn
def get(self): return self.getfn()
def set(self, v): self.setfn(v)
value = property(get, set, None, "reference to local variable")

# change a local variable using the mutable refernce
def mutate(ref, new_value):
ref.value = new_value

def mystery_py_mutable_ref():
x = 1

# get a mutable reference 'ref' to x
def get():
return x
def set(v):
nonlocal x
x = v
ref = Ref(get, set)

# capture x in a closure
capture = lambda: x

# mutate x
mutate(ref, 2)

# finally evaluate x and the closure; this will return 4!
return x * capture()

END DIGRESSION

But anyway, I don't think it's actually relevent here.

Question: Are you familiar with scheme? Would you agree or disagree that the following scm_mystery_a and scm_mystery_b are equivalent to the rust mystery_a and mystery_b functions?

(define scm_mystery_a
(lambda ()
(let ((x 1))
(let ((capture (lambda () x)))
(let ((x 2))
(* x (capture)))))))

(define scm_mystery_b
(lambda ()
(let ((x 1))
(let ((capture (lambda () x)))
(set! x 2)
(* x (capture))))))

(display (scm_mystery_a)) (newline)
(display (scm_mystery_b)) (newline)