Yes, but this goes back to the scoping issue: in python, lambdas (and functions in general) don't capture. The only way to close over something is to pass as an argument. So to get the lexical closure behavior that rust provides, you have to add extra stuff in the python. This indeed makes the translations not mechanical (and you can add the lambda back in the rust, it doesn't hurt anything in these examples), but to get matching scoping behavior between rust and python, you need an extra layer of indirection in the python.
> Bringing in mutable reference data types like vec is I think not really relevent to what's at play here.
Of course it is, because in python everything is a reference. There's no such thing as a value type, and this is precisely where the difference in behavior comes in (other than the scoping issues). A rust RefCell is the thing that most naturally matches the actual in memory representation of a PyObject.
As for your digression, eww, although you forgot to actually do the sneaky part. This would be the actual demonstration, you need to modify the list in the closure (a real closure), and set it after the closure is created and before it is evaluated:
# capture x in a closure
def closure(v):
def inner():
mutate(v, 2)
return v
return inner
capture = closure(ref)
ref.set([3])
> Are you familiar with scheme?Unfortunately not.
And yes, python closures don't capture environemtnal vars like rusts do. If you need them to capture env vars, you have to do what I did in the example.
Wait, I don't follow. Rewriting your example to only use one lambda for clarity, we have:
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:
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:
Which returns 4, showing it's defnly not equivalent. The correct de-lambda-ficiation is: (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()):
END DIGRESSIONBut 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?