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); }
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"); }