وبلاگ شخصی
محمّدرضا
علی حسینی

جایی که تجربیات, علایق
و چیزهایی که یادگرفته‌ام را
با هم مرور می‌کنیم.

تغییرات زبان Rust از نسخه‌ی ۱.۳۱.۰ به ۱.۳۵.۰

تغییرات زبان Rust از نسخه‌ی ۱.۳۱.۰ به ۱.۳۵.۰

زبان Rust زبان جدیدی است و به نسبت زبان‌های دیگر تغییرات در آن زیاد است.

اگر از خوانندگان دوره‌ی آموزش زبان Rust در همین وبلاگ هستید، در این نوشته می‌خواهیم با هم ببینیم که از زمانی که دوره را شروع کردیم تا به امروز چه تغییراتی در زبان Rust رخ داده است.

اگر پیش از تاریخ سیزدهم تیرماه ۱۳۹۸ قسمت‌های ۲،۳ و ۱۲ را خوانده‌اید بهتر است دوباره یک نگاهی به آن‌ها بیندازید. چون به خاطر تغییرات در زبان بخشی از این نوشته‌ها هم عوض شده است.

وحشت نکن!

تغییر panic به error هنگام دادن ایندکس اشتباه به آرایه در 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 طی این ۳ نسخه نبود. امّا بقیه‌ی تغییرات را هنوز در مجموعه‌ی آموزشی ندیده‌ایم و یادشان نگرفته‌ایم.

اگر کنجکاور هستید که باقی تغییرات را هم بخوانید، می‌توانید به صفحات تغییرات نسخه‌ی ۱.۳۲.۰، تغییرات نسخه‌ی ۱.۳۳.۰، تغییرات نسخه‌ی ۱.۳۴.۰ و همینطور صفحه‌ی تغییرات نسخه‌ی ۱.۳۵.۰ سر بزنید.

اگر این مجموعه‌ی آموزشی را تا کنون دنبال نکرده‌اید، می‌توانید با کلیک روی این نوشته به اوّلین قسمت آن بروید و آموزش زبان Rust را از آنجا شروع کنید.

«نوشته‌های مرتبط»

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

«نوشته‌های ویژه»

«نوشته‌های محبوب»

«دیدگاه کاربران»