مقدمه

تاکنون با توابع و کلوژرها به صورت گسترده کار کرده‌ایم. در این درس، به بررسی چند قابلیت پیشرفته‌تر مرتبط با آنها می‌پردازیم. این قابلیت‌ها شامل کار با «اشاره‌گرهای تابع» (function pointers) و نحوه برگرداندن کلوژرها از توابع دیگر است.

اشاره‌گرهای تابع (Function Pointers)

توابع در Rust دارای نوع هستند. این به ما اجازه می‌دهد تا از نام توابع به عنوان مقدار استفاده کرده، آنها را به متغیرها اختصاص دهیم یا به عنوان آرگومان به توابع دیگر پاس دهیم. نوع یک تابع با کلمه کلیدی fn (با حرف کوچک) مشخص می‌شود که آن را از trait مربوط به Fn که برای کلوژرها استفاده می‌شود، متمایز می‌کند.

Copy Icon src/main.rs
fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(add_one, 5);
    println!("The answer is: {}", answer); // 12
}

در اینجا، تابع do_twice یک اشاره‌گر به یک تابع را به عنوان اولین پارامتر خود می‌پذیرد. نوع fn(i32) -> i32 مشخص می‌کند که f باید تابعی باشد که یک i32 گرفته و یک i32 برمی‌گرداند. ما می‌توانیم نام تابع add_one را مستقیماً به آن پاس دهیم.

یک تفاوت کلیدی بین fn و Fn این است که fn یک نوع است، نه یک trait. بنابراین، ما می‌توانیم مستقیماً آن را به عنوان نوع پارامتر مشخص کنیم. کلوژرها می‌توانند به یک fn تبدیل شوند، اما تنها در صورتی که هیچ متغیری را از محیط اطراف خود به ارث نبرند.

برگرداندن کلوژرها

برگرداندن یک کلوژر از یک تابع کمی پیچیده‌تر است، زیرا کلوژرها نوع مشخصی ندارند و سایز آنها در زمان کامپایل معلوم نیست. برای این کار، ما باید از یک trait object در داخل یک اشاره‌گر هوشمند (مانند Box) استفاده کنیم.

Copy Icon src/main.rs
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

fn main() {
    let closure = returns_closure();
    let result = closure(5);
    println!("Result: {}", result); // 6
}

در اینجا، تابع returns_closure یک کلوژر را که در داخل یک Box قرار گرفته، برمی‌گرداند. نوع بازگشتی Box<dyn Fn(i32) -> i32> مشخص می‌کند که ما یک trait object را برمی‌گردانیم که trait Fn(i32) -> i32 را پیاده‌سازی کرده است. این الگو به ما اجازه می‌دهد تا کلوژرها را به صورت دینامیک و بدون نیاز به دانستن نوع دقیق آنها در زمان کامپایل، برگردانیم.

در این درس با مفاهیم پیشرفته‌تری در کار با توابع و کلوژرها، از جمله اشاره‌گرهای تابع و برگرداندن کلوژرها، آشنا شدیم. این قابلیت‌ها قدرت بیان و انعطاف‌پذیری زبان Rust را در پیاده‌سازی الگوهای تابعی افزایش می‌دهند. در درس بعدی و پایانی این فصل، به سراغ یکی از قدرتمندترین ویژگی‌های Rust، یعنی «ماکروها»، خواهیم رفت.