30 روز زنگ زدگی – روز 21

سلام دوستان امروز اولین روزی بود که بعد از چند روز مرخصی سر کار برگشتم. نمی توانم بگویم که از بازگشت هیجان زده هستم، پس از همه اینها تعطیلات خوبی داشتم. اما من برای بازگشت به وبلاگ هیجان زده هستم 💪
کسانی از شما که از روز اول اینجا بوده اید ممکن است احساس کنید که چند پست آخر وبلاگ صرفاً به مفاهیمی پرداخته اند که قبلاً با جزئیات بیشتر به آنها پرداخته بودم. این تا حدی درست است: پس از چرخش به کار بر روی کتاب Rust، من واقعاً برخی از مفاهیم را دوباره بررسی کردم. اما وبلاگ امروز امیدوارم ایده های جدیدی را به روی میز بیاورد 🙌
سوالات دیروز پاسخ داده شد
سوالی برای پاسخگویی وجود ندارد
سوالات باز امروز
بدون سوال باز
مراقب اشتباهاتت باش
بهعنوان توسعهدهنده (و انسانها، حدس میزنم) همه ما اشتباه میکنیم، و امیدوارم در کودکی والدین ما به ما این را روشن کرده باشند که یادگیری از اشتباهات شما یک صفت با فضیلت است. برخی از بزرگترین اشتباهاتی که هنگام کدنویسی مرتکب می شویم ممکن است برای ما کاملاً ناشناخته بماند. کد آزمایش نشده که بدون خطا “اجرا می شود” ممکن است بدون اینکه ما بدانیم کار ناخوشایندی انجام می دهد، به خصوص زمانی که کد ما یک رابط انسانی واضح (مانند یک رابط کاربری) ندارد.
این اغلب یک مشکل در حرفه من، مهندسی داده است. دادههای ورودی میتواند میلیونها نتیجه ممکن داشته باشد، بنابراین آزمایش جامع امکانپذیر نیست. اما نتایج نامنسجم و غیرمحتمل اغلب هنوز می تواند توسط سیستم های پایین دستی مصرف شود، بنابراین یک خط لوله سبز می تواند بسیار گمراه کننده باشد.
هیچ زبان برنامه نویسی نمی تواند تیم های مهندسی را از ایجاد این نوع مشکلات انسانی باز دارد. با این حال، آنها می توانند یک رابط بین انسان و ماشین به شکل یا خطا یا استثنا ارائه دهند. همانطور که ما در مورد Rust صحبت می کنیم، به این موارد اشاره می کنیم خطاها.
زنگ باعث می شود به اشتباهات اهمیت دهید (یا گریه کنید).
قبل از اینکه به بررسی APIهای مختلف کنترل خطا Rust بپردازیم، فکر می کنم استفاده از درک کتاب Rust از خطاها مفید باشد:
برخی از خطاها قابل بازیابی هستند، برخی نه. هنگامی که یک خطا غیر قابل بازیابی باشد، برنامه شما این کار را انجام می دهد وحشت.
حالا ممکن است فکر کنید، این بسیار مبهم است. اما زیربنای چیزی مهم در مورد مدیریت خطا در Rust است: شما باید (تقریبا) همیشه در مورد اعلام خطا به عنوان غیرقابل بازیابی صریح باشید. بیایید از کتاب Rust مثال بزنیم:
let greeting_file_result = File::open("hello.txt");
و آن را با پایتون مقایسه کنید:
greeting_file = pathlib.Path("hello.txt").open("r")
در نگاه اول، این تکه ها بسیار شبیه، تقریباً یکسان به نظر می رسند. اما چه اتفاقی می افتد اگر hello.txt
وجود ندارد؟ در پایتون، کد یک FileNotFoundError ایجاد می کند. در Rust، کد شما بدون خطا کامپایل و اجرا می شود. این به این دلیل است که Rust یک نوع Result را برمیگرداند. این خطای معادل NotFound را جمعآوری میکند، بنابراین میتوانید انتخاب کنید که چه زمانی و کجا آن را در کد خود مدیریت کنید.
اگر واقعاً می خواهید داده های موجود در فایل را در Rust نگه دارید، می توانید باید خطا یا مقدار را از نوع Result استخراج کنید. این بدان معناست که کد شما باید به صراحت یک خطا را کنترل کند تا کامپایل شود. روش های انجام این کار از نظر پرحرفی متفاوت است. بیایید راه حل پیشنهادی برای خطاهایی که برای برنامه ما باید در نظر گرفته شود را بررسی کنیم غیر قابل بازیابی.
تصور کن hello.txt
حاوی اطلاعاتی است که برای برنامه شما حیاتی است و میخواهید این را در خود کد مشخص کنید (بعد از همه، عمو باب نظر را تایید نمیکند). بیایید تکههای کد بالا را مجدداً در نظر بگیریم:
let greeting_file = File::open("hello.txt").expect("Hello file must be available at application startup.");
greeting_file = pathlib.Path("hello.txt")
if not greeting_file.exists():
raise Exception("Hello file must be available at application startup.")
greeting_file = greeting_file.open("r")
# OR
try:
greeting_file = pathlib.Path("hello.txt").open("r")
except FileNotFoundError as e:
raise e
در زنگ .expect
متد از نوع Result به شما امکان می دهد تا آن را فراخوانی کنید panic!
ماکرو با یک پیام خطای سفارشی. کد حاصل بسیار خوب خوانده می شود. آن را با گزینه های خود در پایتون مقایسه کنید. هر دو نیاز به خطوط اضافی کد و تودرتو دارند. همچنین، اگر قوانین سبک تعریف نشده باشند، می توانید یک پایه کد پر از رویکردهای مختلف مدیریت خطا داشته باشید.
نکته این است که Rust در اینجا شما را به سمت اقدامات بهتر هنگام کدنویسی سوق می دهد: به خطاها فکر کنید، صریح باشید و خطاها را به روشی قابل خواندن منتشر کنید.
کاهش دیگ بخار نوع برگشتی
مثال بالا نشان می دهد که چگونه می توان وحشت ایجاد کرد. به طور واقع بینانه، ما نمی خواهیم این کار را در اکثر مواردی که یک تابع یک نوع نتیجه را برمی گرداند، انجام دهیم. بیشتر اوقات ما انتظار داریم که ارزشی وجود داشته باشد.
بنابراین استخراج یک مقدار از طریق بیانیه مطابقت می تواند خسته کننده و طولانی باشد. مثال refactored را در نظر بگیرید که دوباره با استفاده از یک دستور مطابقت مجدداً بازسازی شده است:
let greeting_file_result = File::open("hello.txt")
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => create_hello_file(),
}
تصور کنید اگر فایل وجود نداشت، به جای ایجاد وحشت، میخواهیم یک فایل ایجاد کنیم. تصویر پس از آن که create_hello_file
تابع همچنین یک نوع Result را برمی گرداند. در نهایت با یک عبارت تطبیق تودرتو بسیار زشت مواجه می شوید. وارد ?
اپراتور.
از آنجایی که چنین موردی در کد Rust بسیار رایج است، زبان دارای آن است ?
اپراتور. استفاده از آن شبیه به جاوا اسکریپت به نظر می رسد ?
اپراتور. تفاوت این است که ?
به جای خطاها را می گیرد undefined
. بنابراین اگر ما به زنجیر create_hello_file
عملکرد با ?
، هر گونه خطا را تا دستور مطابقت منتشر می کند.
اپراتور زنجیره ای روشی را برای ترکیب فراخوانی تابع یا روش ارائه می دهد به طوری که وقتی برای هر تماس، نتیجه مورد انتظار برگردانده شد، تابع فراخوانی نتیجه را برمی گرداند. به طور همزمان، هر گونه خطا به طور خودکار با نتیجه برگردانده می شود، با این فرض که نوع خطا با امضای تابع فراخوان سازگار است.
افتراق بین خطاها
می توانید از enums برای ایجاد انواع یا خطاها استفاده کنید. این به این معنی است که شما می توانید انواع خطاهای خاص را در دستورات مطابقت مطابقت دهید و بسته به اینکه خطا چیست، کارهای مختلفی انجام دهید. همچنین می توانید از دستور if برای انجام همین کار استفاده کنید.
زمان بازگشت نتایج
این بسیار ساده است:
وقتی شک دارید، به جای وحشت، نتیجه را برگردانید.
این باعث می شود کد شما قابل استفاده مجدد باشد، زیرا تابع فراخوان باید تصمیم بگیرد که با نتیجه چه کاری انجام دهد.
استثنا زمانی است که آرگومان ها به معنای الزامات نیستند (مثلاً مثبت بودن ints). سپس شما باید وحشت کنید.