تغییرات زبان Rust از نسخهی ۱.۳۱.۰ به ۱.۳۵.۰
زبان Rust زبان جدیدی است و به نسبت زبانهای دیگر تغییرات در آن زیاد است.
اگر از خوانندگان دورهی آموزش زبان Rust در همین وبلاگ هستید، در این نوشته میخواهیم با هم ببینیم که از زمانی که دوره را شروع کردیم تا به امروز چه تغییراتی در زبان Rust رخ داده است.
اگر پیش از تاریخ سیزدهم تیرماه ۱۳۹۸ قسمتهای ۲،۳ و ۱۲ را خواندهاید بهتر است دوباره یک نگاهی به آنها بیندازید. چون به خاطر تغییرات در زبان بخشی از این نوشتهها هم عوض شده است.
فهرست مطالب
وحشت نکن!
در نسخههای قبلی اگر index آرایه را اشتباه و خارج از بازهی مورد قبول وارد میکردیم، موقع کامپایل هیچ خطایی دریافت نمیکردیم. به جایش هنگام اجرای برنامه، برنامه panic
میکرد.
مثلاً اگر کد زیر را اجرا میکردیم:
fn main() { let my_array = [0, 1, 2]; println!("{}", my_array[3]); }
موقع «اجرا» با این پیام مواجه میشدیم:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 3', src/main.rs:3:20 stack backtrace: 0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49 1: std::sys_common::backtrace::print at libstd/sys_common/backtrace.rs:71 at libstd/sys_common/backtrace.rs:59 2: std::panicking::default_hook::{{closure}} at libstd/panicking.rs:211 3: std::panicking::default_hook at libstd/panicking.rs:227 4: std::panicking::rust_panic_with_hook at libstd/panicking.rs:463 5: std::panicking::begin_panic_fmt at libstd/panicking.rs:350 6: rust_begin_unwind at ibstd/panicking.rs:328 7: بقیهی متنرا چون طولانی بود حذف کردم.
امّا اگر الان با نسخهی ۱.۳۵ همین برنامه را بخواهیم کامپایل کنیم، این بار زمان کامپایل با خطای زیر روبهرو میشویم:
error: index out of bounds: the len is 3 but the index is 3 --> src/main.rs:5:9 | 5 | my_array[3]; | ^^^^^^^^^^^ | = note: #[deny(const_err)] on by default
تفاوتش چیست؟ اینکه وقتی برنامه کامپایل میشود خطایش را بگیریم خیلی تفاوت دارد با اینکه هنگام اجرای برنامه، برنامه یکدفعه panic کند. در حالت دوم ممکن است هنگامی که کاربر دارد از برنامه استفاده میکند ناگهان با خطا مواجه شود. خطایی که مسببش کاربر نیست.
البته هنوز هم اگر index را از طریق ورودی از کاربر سیستم بگیریم، اگر مقدار اشتباه باشد برنامه panic میکند. ولی حداقل هنگامی که index ها را به صورت hardcode شده استفاده کردهایم، میتوانیم مطمئن باشیم که برنامه درست کار میکند.
حواست باشد که سرریز نکند
یک چیز دیگر که در نسخههای پیش دیدیم این بود که وقتی در مقادیر عددی سرریز (overflow) رخ میداد، کامپایلر جلوی کامپایل شدن برنامه را نمیگرفت. صرفاً یک اخطار (warning) میداد و در آن میگفت که امکان دارد سرریز رخداده باشد.
مثلاً شما میتوانستید در نسخههای قدیمیتر این کد را اجرا کنید:
fn main() { let a:i8 = 0xf_f; println!("{}\n", a); }
با اجرای این برنامه شما صرفاً این اخطار را دریافت میکردید. امّا برنامه اجرا میشد و مقدار ۱− را چاپ میکرد.
warning: literal out of range for i8 --> src/main.rs:2:16 | 2 | let a:i8 = 0xf_f; | ^^^^^ | = note: #[warn(overflowing_literals)] on by default = note: the literal `0xf_f` (decimal `255`) does not fit into an `i8` and will become `-1i8` = help: consider using `u8` instead
امّا الان اگر این برنامه را اجرا کنید، کامپایلر جلوی کامپایل شدنش را با خطای زیر میگیرد:
error: literal out of range for i8 --> src/main.rs:3:16 | 3 | let a:i8 = 0xf_f; | ^^^^^ | = note: #[deny(overflowing_literals)] on by default = note: the literal `0xf_f` (decimal `255`) does not fit into an `i8` and will become `-1i8` = help: consider using `u8` instead
همانطوری که در متن خطا میبینید، کامپایلر از ما میخواهد که از نوع دادهای با بازهی بزرگتر، مثل u8
استفاده کنیم.
همانطوری که در متن خطا هم توضیح داده شده است، پیش از این به صورت پیشفرض overflow_literals
اجازهی اجرا داشتند. امّا در حال حاضر این ویژگی تغییر کرده است و شما نمیتوانید از آن استفاده کنید.
اگر در کدتان نیازدارید که حتماً سرریز رخ بدهید، میتوانید با افزودن این خط به برنامهیتان به کامپایلر بگویید که این موضوع را بررسی نکند:
#[allow(overflowing_literals)]
درمورد لزوم پیشفرض بودن این ویژگی در جامعهی کاربری Rust صحبتهای زیادی شده است. برای مثال میتوانید این issue را دنبال کنید یا این pull request را بخوانید.
کپی کردن علامت عدد اعشاری
در نسخهی ۱.۳۵.۰ یک متد جدید به اعداد اعشاری (نوعهای f32
و f64
) اضافه شده است. شما میتوانید علامت یک عدد اعشاری را روی دیگری کپی کنید.
مثلاً کد زیر را ببینید:
fn main() { let negative_float = -3.16; println!("{}", 5.5_f32.copysign(negative_float)); }
اگر این برنامه را اجرا کنیم، خروجی زیر را دریافت میکنیم:
-5.5
همانطوری که میبینید، برای فراخوانی متدها روی اعداد اعشاری، پس از نوشتن عدد باید علامت _ را بگذاریم و بعد از آن نوع عدد اعشاری (f32
یا f64
) را مشخّص کنیم. بعد از نوشتن نوع هم مثل بقیهی متدها خیلی راحت بعد از علامت نقطه، اسم متد را میآوریم.
ببخشید، این بچه برای شماست؟
یک اتّفاق دیگر که در نسخهی ۱.۳۵.۰ افتاده این است که شما با اضافه شدن متد contains
به بازهها، میتوانید بررسی کنید که آیا یک مقدار درون یک بازه (range) قرار دارد یا نه.
مثلاً کد زیر را ببینید:
fn main() { if (0..=10).contains(&5) { println!("Five is included in zero to ten."); } }
با اجرای این کد، که از وبلاگ Rust برش داشته ام، متن درون println
چاپ میشود.
همانطوری که میبینید با فراخوانی متد contains
و ارسال یک reference به درون آن، میتوانیم این موضوع را بررسی کنیم که آیا این بازه شامل مقداری که رفرنس به آن اشاره میکند میشود یا نه.
فقط حواستان باشد که برخلاف بقیهی جاها که گذاشتن پرانتز دور بازه لازم نبود، اینجا حتماً باید بازه را درون پرانتز قرار بدهیم. در غیر این صورت کامپایلر تلاش میکند تا متد را روی عدد ۱۰ اجرا کند، نه بازهی ۰ تا ۱۰.
مطالبی که در این نوشته آوردم تمام تغییرات زبان Rust طی این ۳ نسخه نبود. امّا بقیهی تغییرات را هنوز در مجموعهی آموزشی ندیدهایم و یادشان نگرفتهایم.
اگر کنجکاور هستید که باقی تغییرات را هم بخوانید، میتوانید به صفحات تغییرات نسخهی ۱.۳۲.۰، تغییرات نسخهی ۱.۳۳.۰، تغییرات نسخهی ۱.۳۴.۰ و همینطور صفحهی تغییرات نسخهی ۱.۳۵.۰ سر بزنید.
یا حرفهای شو یا برنامهنویسی را رها کن.
چطور میشود در بازار کار خالی از نیروی سنیور ایران، بدون کمک دیگران و وقت تلفکردن خودت را تبدیل به یک نیروی باتجربه بکنی؟
پاسخ ساده است. باید حرفهای بشوی. چیزی که من در این کتاب به تو یاد خواهم داد.