Variable Closure Return

I am trying to build a solution for Battery Graham factory challenge , which basically requires a function to return a closing closing over a variable numerical variable, the initial value of which is obtained through the parameter. Each call to this closure increments this captured variable by the value that is the closure parameter and returns the accumulated value.

After reading, it closes the RFC and some questions about returning unpacked closures (in particular this ). I could finally come up with a solution that compiled, but the result is not the one I would expect.

#![feature(unboxed_closures, unboxed_closure_sugar)] fn accumulator_factory(n: f64) -> Box<|&mut: f64| -> f64> { let mut acc = n; box |&mut: i: f64| { acc += i; acc } } fn main() { let mut acc_cl = accumulator_factory(5f64); println!("{}", acc_cl.call_mut((3f64,))); println!("{}", acc_cl.call_mut((3f64,))); } 

AFAIK this closure captures acc by value, the generated structure that acts as the environment is changed, and acc_cl must keep the same instance of the environment between calls.

But the printed result is 6 in both cases, so it seems that the changed value is not saved. And the more confusing is how this result is calculated. Each time the closure is performed, the initial value of acc is 3 , although n is 5 when called.

If I change the generator to this:

 fn accumulator_factory(n: f64) -> Box<|&mut: f64| -> f64> { println!("n {}", n); let mut acc = n; box |&mut: i: f64| { acc += i; acc } } 

then execution always returns 3 , and the initial value acc always 0 in the close record.

This difference in semantics looks like a mistake. But also, why does the environment reset between calls?

This was accomplished with rustc 0.12.0.

+6
source share
1 answer

Short circuit capture mode has recently changed. Closures are biased to capture everything by reference, because the most common use case for closures is passing them to functions down the call stack, and capturing by reference makes working with the environment more natural.

Sometimes capturing by reference is restrictive. For example, you cannot return such locks from functions because their environment is tied to a call stack. For such closures, you need to put the keyword move before closing:

 fn accumulator_factory(n: f64) -> Box<FnMut(f64) -> f64> { println!("n: {}", n); let mut acc = n; Box::new(move |i: f64| { acc += i; acc }) } fn main() { let mut acc = accumulator_factory(10.0); println!("{}", acc(12.0)); println!("{}", acc(12.0)); } 

This program works as intended:

 n: 10 22 34 

These two types of closure are covered by this RFC.

+8
source

Source: https://habr.com/ru/post/977083/


All Articles