Closures

بسته‌ها یا عبارات لامبدا تایپ‌هایی دارند که نمی‌توان نام‌گذاری کرد. با این حال، آن‌ها پیاده‌سازی‌های ویژه از traits Fn، FnMut و FnOnce هستند:

fn apply_and_log(func: impl FnOnce(i32) -> i32, func_name: &str, input: i32) {
    println!("فراخوانی {func_name}({input}): {}", func(input))
}

fn main() {
    let n = 3;
    let add_3 = |x| x + n;
    apply_and_log(&add_3, "add_3", 10);
    apply_and_log(&add_3, "add_3", 20);

    let mut v = Vec::new();
    let mut accumulate = |x: i32| {
        v.push(x);
        v.iter().sum::<i32>()
    };
    apply_and_log(&mut accumulate, "تجمیع: {}", 4);
    apply_and_log(&mut accumulate, "تجمیع: {}", 5);

    let multiply_sum = |x| x * v.into_iter().sum::<i32>();
    apply_and_log(multiply_sum, "multiply_sum", 3);
}
This slide should take about 10 minutes.

An Fn (e.g. add_3) neither consumes nor mutates captured values. It can be called needing only a shared reference to the closure, which means the closure can be executed repeatedly and even concurrently.

An FnMut (e.g. accumulate) might mutate captured values. The closure object is accessed via exclusive reference, so it can be called repeatedly but not concurrently.

If you have an FnOnce (e.g. multiply_sum), you may only call it once. Doing so consumes the closure and any values captured by move.

FnMut یک زیرتایپ از FnOnce است. Fn نیز یک زیرتایپ از FnMut و FnOnce است. به عبارت دیگر، می‌توانید از FnMut در جایی که FnOnce نیاز است استفاده کنید و از Fn در جایی که FnMut یا FnOnce نیاز است استفاده کنید.

زمانی که تابعی تعریف می‌کنید که یک closure را می‌گیرد، باید از FnOnce استفاده کنید اگر فقط یک بار آن را فراخوانی می‌کنید (یعنی یک بار استفاده می‌شود)، یا از FnMut در غیر این صورت، و در نهایت از Fn. این کار بیشترین انعطاف‌پذیری را برای فراخوانی‌کننده فراهم می‌کند.

در مقابل، زمانی که یک closure دارید، بیشترین انعطاف‌پذیری که می‌توانید داشته باشید Fn است (که می‌تواند در هر جایی استفاده شود)، سپس FnMut و در نهایت FnOnce.

The compiler also infers Copy (e.g. for add_3) and Clone (e.g. multiply_sum), depending on what the closure captures. Function pointers (references to fn items) implement Copy and Fn.

به صورت پیش‌فرض، بسته‌بندی‌ها (closures) هر متغیر از یک دامنه بیرونی را با کمترین سطح دسترسی ممکن (با ارجاع مشترک اگر ممکن باشد، سپس ارجاع انحصاری، سپس با انتقال) capture می‌کنند. کلیدواژه move یا انتقال، capture را به صورت value اجباری می‌کند.

fn make_greeter(prefix: String) -> impl Fn(&str) {
    return move |name| println!("{} {}", prefix, name);
}

fn main() {
    let hi = make_greeter("Hi".to_string());
    hi("Greg");
}