
روز ۶: توابع در Rust و نحوه تعریف آنها
توابع یکی از اساسیترین مفاهیم در هر زبان برنامه نویسی هستند و در Rust نقش بسیار مهمتری نسبت به چیزی که در نگاه اول به نظر میرسد دارند. در روز ششم از سری آموزش «Rust در ۵۰ روز» وارد مبحث توابع میشویم و یاد میگیریم چگونه منطق برنامه را به بخشهای کوچکتر، خواناتر و قابل استفاده مجدد تقسیم کنیم. Rust با طراحی خاص خود در تعریف توابع، توسعهدهنده را به نوشتن کدهای تمیز، قابل پیشبینی و امن تشویق میکند و این موضوع تأثیر مستقیمی روی کیفیت پروژههای بزرگ دارد.
برخلاف زبانهایی که انعطاف بیش از حد در تعریف توابع دارند و گاهی باعث بروز رفتارهای مبهم میشوند، Rust در این بخش بسیار صریح و شفاف عمل میکند. نوع پارامترها، نوع مقدار بازگشتی و حتی نحوه پایان یافتن تابع همگی قواعد مشخصی دارند. همین شفافیت باعث میشود کامپایلر Rust بتواند خطاهای زیادی را قبل از اجرای برنامه شناسایی کند و از بروز باگهای منطقی جلوگیری شود. در این مقاله قدمبهقدم با ساختار توابع در Rust آشنا میشویم.
تعریف تابع در Rust با کلمه کلیدی fn
برای تعریف تابع در Rust از کلمه کلیدی fn استفاده میشود که بعد از آن نام تابع، پارامترها و بدنه تابع قرار میگیرد. نامگذاری توابع معمولاً با حروف کوچک و بهصورت snake_case انجام میشود که یکی از استانداردهای مهم کدنویسی در Rust است. این استاندارد باعث میشود کدها در تمام پروژهها یکدست و خوانا باشند، حتی اگر توسط افراد مختلف نوشته شده باشند.
در Rust باید نوع تمام پارامترهای ورودی تابع بهصورت صریح مشخص شود و این موضوع یکی از تفاوتهای مهم آن با زبانهایی مثل Python است. این اجبار باعث میشود رفتار تابع کاملاً مشخص باشد و کامپایلر بتواند از بروز خطاهای تایپی جلوگیری کند. تعریف شفاف توابع یکی از پایههای سیستم تایپ قدرتمند Rust محسوب میشود.
fn greet() {
println!("Hello, Rust!");
}در این مثال یک تابع ساده تعریف شده که هیچ پارامتری دریافت نمیکند و فقط یک پیام را چاپ میکند. همین ساختار ساده پایه تمام توابع پیچیدهتر در Rust است.
پارامترها در توابع Rust
توابع در Rust میتوانند یک یا چند پارامتر دریافت کنند و برای هر پارامتر باید نوع داده بهصورت مشخص تعریف شود. این موضوع باعث میشود هنگام استفاده از تابع، توسعهدهنده دقیقاً بداند چه نوع دادهای باید ارسال کند. Rust اجازه نمیدهد پارامترها بهصورت ضمنی نوعگذاری شوند و این سختگیری به افزایش ایمنی کد کمک میکند.
ترتیب پارامترها در تعریف تابع اهمیت دارد و هنگام فراخوانی باید دقیقاً همان ترتیب رعایت شود. همچنین Rust به شما اجازه میدهد پارامترهایی با انواع مختلف داشته باشید، به شرطی که نوع آنها مشخص باشد. این شفافیت در تعریف پارامترها باعث میشود توابع بهراحتی قابل فهم و قابل استفاده مجدد باشند.
fn add(a: i32, b: i32) {
println!("{}", a + b);
}در این مثال تابع add دو پارامتر عدد صحیح دریافت میکند و حاصل جمع آنها را چاپ میکند.
مقدار بازگشتی توابع در Rust
یکی از ویژگیهای مهم توابع در Rust نحوه بازگرداندن مقدار است. در Rust نوع مقدار بازگشتی باید بهصورت صریح بعد از علامت -> مشخص شود. این موضوع باعث میشود خروجی تابع از همان ابتدا مشخص باشد و هیچ ابهامی در رفتار آن وجود نداشته باشد. این ویژگی نقش مهمی در ایمنی و خوانایی کد دارد.
در Rust برای بازگرداندن مقدار، معمولاً از expression استفاده میشود نه دستور return. به این معنا که آخرین عبارت بدون سمیکالن بهعنوان خروجی تابع در نظر گرفته میشود. البته استفاده از return همچنان امکانپذیر است، اما در بسیاری از موارد نیازی به آن نیست. این سبک نوشتار باعث میشود کدها کوتاهتر و خواناتر باشند.
fn sum(a: i32, b: i32) -> i32 {
a + b
}در این تابع مقدار جمع دو عدد بهعنوان خروجی بازگردانده میشود و نیازی به نوشتن return نیست.
تفاوت statement و expression در توابع Rust
درک تفاوت بین statement و expression یکی از کلیدهای فهم توابع در Rust است. statementها دستوراتی هستند که کاری انجام میدهند اما مقدار بازنمیگردانند، در حالی که expressionها هم اجرا میشوند و هم مقدار تولید میکنند. Rust بهشدت بر پایه expression طراحی شده و این موضوع در توابع کاملاً مشخص است.
وقتی آخرین خط یک تابع یک expression باشد و به سمیکالن ختم نشود، Rust آن را بهعنوان مقدار بازگشتی در نظر میگیرد. این رویکرد باعث میشود توابع Rust ساختاری تمیز و منطقی داشته باشند. درک این مفهوم در ادامه مسیر یادگیری Rust، مخصوصاً در بخشهای پیشرفتهتر، اهمیت زیادی خواهد داشت.
ترتیب تعریف و فراخوانی توابع در Rust
برخلاف برخی زبانها، در Rust مهم نیست که تابع قبل یا بعد از محل فراخوانی تعریف شده باشد. کامپایلر Rust کل فایل را بررسی میکند و توابع را شناسایی میکند. این موضوع باعث میشود آزادی عمل بیشتری در سازماندهی کد داشته باشید و بتوانید توابع را بر اساس منطق برنامه مرتب کنید نه ترتیب اجرا.
این ویژگی بهخصوص در پروژههای بزرگ بسیار مفید است، چون میتوانید توابع اصلی را در ابتدای فایل قرار دهید و جزئیات پیادهسازی را به بخشهای پایینتر منتقل کنید. چنین ساختاری باعث افزایش خوانایی و نگهداریپذیری کد میشود.
بهترین شیوه استفاده از توابع در Rust
در Rust توصیه میشود توابع کوچک، مشخص و با یک مسئولیت واضح نوشته شوند. این رویکرد باعث میشود تستنویسی سادهتر شود و در صورت بروز خطا، محل مشکل سریعتر شناسایی شود. توابع بزرگ و چندمنظوره معمولاً خوانایی کد را کاهش میدهند و برخلاف فلسفه طراحی Rust هستند.
استفاده درست از توابع به شما کمک میکند از تکرار کد جلوگیری کنید و منطق برنامه را بهصورت ماژولار پیادهسازی کنید. Rust با سیستم تایپ قوی و قوانین سختگیرانه خود، شما را به سمت نوشتن چنین کدهایی هدایت میکند، حتی اگر در ابتدا این سختگیری کمی آزاردهنده به نظر برسد.
جمعبندی روز ششم آموزش Rust
در روز ششم یاد گرفتیم توابع در Rust چگونه تعریف میشوند، پارامترها و مقادیر بازگشتی چه قوانینی دارند و چرا Rust در این بخش تا این حد شفاف و سختگیر است. توابع پایه اصلی ساختار برنامههای Rust هستند و درک درست آنها برای ادامه مسیر یادگیری کاملاً ضروری است. از این نقطه به بعد، تقریباً در تمام مثالها و پروژهها با توابع سر و کار خواهیم داشت.
در روز بعدی وارد مبحث شرطها و کنترل جریان در Rust میشویم و یاد میگیریم چگونه با استفاده از if و match منطق تصمیمگیری را در برنامه پیادهسازی کنیم. اینجاست که Rust کمکم چهره واقعی خودش را نشان میدهد.