برنامه نویسی

تسلط بر رسیدگی به خطای در زنگ زدگی: فراتر از نتیجه و گزینه

پوشش

رسیدگی به خطا در زنگ به همان اندازه ساده نیست Result وت Optionبشر برای مبتدیان ، رسیدگی به خطای Rust می تواند کاملاً دوستانه باشد. بعد از چندین بار تلاش با آن ، تصمیم گرفتم دانش خود را در مورد این موضوع سازماندهی کنم. این راهنما از دو بخش اصلی تشکیل شده است:

  1. روشهای رسمی برای کار با Result
  2. نحوه تعریف و رسیدگی به خطاهای سفارشی.

تسلط بر این مفاهیم به شما کمک می کند تا بر چالش های خطای زنگ زدگی غلبه کنید.

روشهای رسیدگی به خطا

برای رسیدگی به خطاها به طور موثر ، باید از روشهای داخلی Rust استفاده کنید. این کار شما را بسیار ساده تر می کند.

برخی از روشهای مفید عبارتند از:

  • or()
  • and()
  • or_else()
  • and_then()
  • map()
  • map_err()
  • map_or()
  • map_or_else()
  • ok_or()
  • ok_or_else()

در زیر ، من توضیح خواهم داد که چه موقع از این روش ها ، نحوه استفاده از آنها و در نهایت نحوه طراحی خود استفاده کنید Err انواع هنگام نوشتن کد.

or() وت and()

این روشها به شما امکان می دهد بین دو گزینه ، مشابه منطقی یا و و و و و و و و و.

  • or(): عبارات را به ترتیب ارزیابی می کند. اگر هر بیان منجر شود Some یا Ok، این مقدار بلافاصله برگردانده می شود.
  • and(): در صورت وجود هر دو مقدار را از عبارت دوم برمی گرداند Some یا Okبشر اگر هر نتیجه ای باشد None یا Err، در عوض آن را برمی گرداند.
let s1 = Some("some1");
let s2 = Some("some2");
let n: Option<&str> = None;

let o1: Result<&str, &str> = Ok("ok1");
let o2: Result<&str, &str> = Ok("ok2");
let e1: Result<&str, &str> = Err("error1");
let e2: Result<&str, &str> = Err("error2");

assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1
assert_eq!(s1.or(n), s1);  // Some or None = Some
assert_eq!(n.or(s1), s1);  // None or Some = Some
assert_eq!(n.or(n), n);    // None1 or None2 = None2

assert_eq!(o1.or(o2), o1); // Ok1 or Ok2 = Ok1
assert_eq!(o1.or(e1), o1); // Ok or Err = Ok
assert_eq!(e1.or(o1), o1); // Err or Ok = Ok
assert_eq!(e1.or(e2), e2); // Err1 or Err2 = Err2

assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2
assert_eq!(s1.and(n), n);  // Some and None = None
assert_eq!(n.and(s1), n);  // None and Some = None
assert_eq!(n.and(n), n);   // None1 and None2 = None1

assert_eq!(o1.and(o2), o2); // Ok1 and Ok2 = Ok2
assert_eq!(o1.and(e1), e1); // Ok and Err = Err
assert_eq!(e1.and(o1), e1); // Err and Ok = Err
assert_eq!(e1.and(e2), e1); // Err1 and Err2 = Err1
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

or_else() وت and_then()

در or() وت and() روشها فقط بین دو مقدار انتخاب می کنند ، بدون اینکه آنها را اصلاح کنند. اگر نیاز به استفاده از منطق پیچیده تر دارید ، باید از بسته شدن استفاده کنید or_else() وت and_then()بشر

// Using or_else() with Option
let s1 = Some("some1");
let s2 = Some("some2");
let fn_some = || Some("some2");  // Equivalent to: let fn_some = || -> Option<&str> { Some("some2") };

let n: Option<&str> = None;
let fn_none = || None;

assert_eq!(s1.or_else(fn_some), s1);  // Some1 or_else Some2 = Some1
assert_eq!(s1.or_else(fn_none), s1);  // Some or_else None = Some
assert_eq!(n.or_else(fn_some), s2);  // None or_else Some = Some
assert_eq!(n.or_else(fn_none), None); // None1 or_else None2 = None2

// Using or_else() with Result
let o1: Result<&str, &str> = Ok("ok1");
let o2: Result<&str, &str> = Ok("ok2");
let fn_ok = |_| Ok("ok2");

let e1: Result<&str, &str> = Err("error1");
let e2: Result<&str, &str> = Err("error2");
let fn_err = |_| Err("error2");

assert_eq!(o1.or_else(fn_ok), o1);  // Ok1 or_else Ok2 = Ok1
assert_eq!(o1.or_else(fn_err), o1);  // Ok or_else Err = Ok
assert_eq!(e1.or_else(fn_ok), o2);  // Err or_else Ok = Ok
assert_eq!(e1.or_else(fn_err), e2);  // Err1 or_else Err2 = Err2
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

map()

اگر می خواهید مقدار داخل را تغییر دهید Result یا Option، استفاده کنید map()بشر

let s1 = Some("abcde");
let s2 = Some(5);

let n1: Option<&str> = None;
let n2: Option<usize> = None;

let o1: Result<&str, &str> = Ok("abcde");
let o2: Result<usize, &str> = Ok(5);

let e1: Result<&str, &str> = Err("abcde");
let e2: Result<usize, &str> = Err("abcde");

let fn_character_count = |s: &str| s.chars().count();

assert_eq!(s1.map(fn_character_count), s2); // Some1 map = Some2
assert_eq!(n1.map(fn_character_count), n2); // None1 map = None2

assert_eq!(o1.map(fn_character_count), o2); // Ok1 map = Ok2
assert_eq!(e1.map(fn_character_count), e2); // Err1 map = Err2
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

map_err()

در صورت نیاز به اصلاح Err ارزش در Result، استفاده کنید map_err()بشر

let o1: Result<&str, &str> = Ok("abcde");
let o2: Result<&str, isize> = Ok("abcde");

let e1: Result<&str, &str> = Err("404");
let e2: Result<&str, isize> = Err(404);

let fn_character_count = |s: &str| -> isize { s.parse().unwrap() };

assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2
assert_eq!(e1.map_err(fn_character_count), e2); // Err1 map = Err2
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

map_or()

اگر مطمئن هستید که هیچ وجود نخواهد داشت Err، می توانید استفاده کنید map_or() به جای آن مقدار پیش فرض را برگردانید.

const V_DEFAULT: u32 = 1;

let s: Result<u32, ()> = Ok(10);
let n: Option<u32> = None;
let fn_closure = |v: u32| v + 2;

assert_eq!(s.map_or(V_DEFAULT, fn_closure), 12);
assert_eq!(n.map_or(V_DEFAULT, fn_closure), V_DEFAULT);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

map_or_else()

map_or() فقط اجازه می دهد تا یک مقدار پیش فرض را برگردانید ، اما اگر برای تهیه پیش فرض به بسته شدن نیاز دارید ، استفاده کنید map_or_else()بشر

let s = Some(10);
let n: Option<i8> = None;

let fn_closure = |v: i8| v + 2;
let fn_default = || 1;

assert_eq!(s.map_or_else(fn_default, fn_closure), 12);
assert_eq!(n.map_or_else(fn_default, fn_closure), 1);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ok_or()

اگر می خواهید یک را تبدیل کنید Option به Result، می توانید استفاده کنید ok_or()بشر

const ERR_DEFAULT: &str = "error message";

let s = Some("abcde");
let n: Option<&str> = None;

let o: Result<&str, &str> = Ok("abcde");
let e: Result<&str, &str> = Err(ERR_DEFAULT);

assert_eq!(s.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T)
assert_eq!(n.ok_or(ERR_DEFAULT), e); // None -> Err(default)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ok_or_else()

هنگام برخورد با پتانسیل Err موارد و مایل به بازگشت خطایی از همان نوع با استفاده از بسته شدن ، استفاده ok_or_else()بشر

let s = Some("abcde");
let n: Option<&str> = None;
let fn_err_message = || "error message";

let o: Result<&str, &str> = Ok("abcde");
let e: Result<&str, &str> = Err("error message");

assert_eq!(s.ok_or_else(fn_err_message), o); // Some(T) -> Ok(T)
assert_eq!(n.ok_or_else(fn_err_message), e); // None -> Err(default)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نحوه طراحی خطا

مبتدیان غالباً از انواع خطای سخت Rust ناامید می شوند ، به خصوص هنگامی که با عدم تطابق نوع در چندین مورد روبرو می شوند Result برمی گردد با درک Result انواع عمیق تر ، می توانید از این ناامیدی ها جلوگیری کنید.

تعریف خطاهای سفارشی ساده

هنگام نوشتن برنامه ها ، تعریف خطاهای سفارشی معمول است. در اینجا مثالی از یک رسم ساده آورده شده است Result:

use std::fmt;

// CustomError is a user-defined error type.
#[derive(Debug)]
struct CustomError;

// Implementing the Display trait for user-facing error messages.
impl fmt::Display for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "An Error Occurred, Please Try Again!")
    }
}

// Example function that generates a CustomError.
fn make_error() -> Result<(), CustomError> {
    Err(CustomError)
}

fn main(){
    match make_error() {
        Err(e) => eprintln!("{}", e),
        _ => println!("No error"),
    }

    eprintln!("{:?}", make_error());
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

توجه: در eprintln! ماکرو برای خروجی خطا استفاده می شود ، اما عملکرد مشابه دارد println! مگر اینکه خروجی هدایت شود.

با اجرای Debug وت Display، نه تنها می توانید خطاها را برای صفحه نمایش قالب بندی کنید ، بلکه می توانید خطاهای سفارشی را نیز تبدیل کنید Box اشیاء صفت.

تعریف خطاهای پیچیده تر

در سناریوهای دنیای واقعی ، ما اغلب کدها و پیام های خطا را اختصاص می دهیم:

use std::fmt;

struct CustomError {
    code: usize,
    message: String,
}

// Display different error messages based on the code.
impl fmt::Display for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let err_msg = match self.code {
            404 => "Sorry, Cannot find the Page!",
            _ => "Sorry, something is wrong! Please Try Again!",
        };
        write!(f, "{}", err_msg)
    }
}

impl fmt::Debug for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "CustomError {{ code: {}, message: {} }}",
            self.code, self.message
        )
    }
}

fn make_error() -> Result<(), CustomError> {
    Err(CustomError {
        code: 404,
        message: String::from("Page not found"),
    })
}

fn main() {
    match make_error() {
        Err(e) => eprintln!("{}", e),
        _ => println!("No error"),
    }

    eprintln!("{:?}", make_error());
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اجرای دستی از Display وت Debug اجازه می دهد تا خروجی سفارشی تر از استفاده #[derive(Debug)]بشر

تبدیل خطا

اگر از کتابخانه های شخص ثالث استفاده می کنید ، هر کدام انواع خطای خاص خود را تعریف می کنند؟ زنگ خطر را فراهم می کند std::convert::From صفت برای تبدیل خطا.

use std::fs::File;
use std::io;

#[derive(Debug)]
struct CustomError {
   kind: String,
   message: String,
}

// Convert `io::Error` into `CustomError`.
impl From<io::Error> for CustomError {
   fn from(error: io::Error) -> Self {
       CustomError {
           kind: String::from("io"),
           message: error.to_string(),
       }
   }
}

fn main() -> Result<(), CustomError> {
   let _file = File::open("nonexistent_file.txt")?;
   Ok(())
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در ? اپراتور به طور خودکار تبدیل می کند std::io::Error به CustomErrorبشر این رویکرد به طور قابل توجهی رسیدگی به خطا را ساده می کند.

رسیدگی به انواع مختلف خطای

اگر عملکرد شما با انواع مختلف خطای سروکار داشته باشد ، چه می شود؟

use std::fs::File;
use std::io::{self, Read};
use std::num;

#[derive(Debug)]
struct CustomError {
    kind: String,
    message: String,
}

impl From<io::Error> for CustomError {
    fn from(error: io::Error) -> Self {
        CustomError {
            kind: String::from("io"),
            message: error.to_string(),
        }
    }
}

impl From<num::ParseIntError> for CustomError {
    fn from(error: num::ParseIntError) -> Self {
        CustomError {
            kind: String::from("parse"),
            message: error.to_string(),
        }
    }
}

fn main() -> Result<(), CustomError> {
    let mut file = File::open("hello_world.txt")?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    let _number: usize = content.parse()?;
    Ok(())
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

استراتژی های پیشرفته کنترل خطای

هنگامی که توابع انواع مختلف خطای مختلف را برمی گردانند ، در اینجا چهار رویکرد مشترک وجود دارد:

1. استفاده از Box

این روش انواع خطا را به یک شیء صفت تبدیل می کند.

use std::fs::read_to_string;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let html = render()?;
    println!("{}", html);
    Ok(())
}

fn render() -> Result<String, Box<dyn Error>> {
    let file = std::env::var("MARKDOWN")?;
    let source = read_to_string(file)?;
    Ok(source)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

جوانب مثبت: کد را ساده می کند.

منفی ها: از دست دادن عملکرد جزئی و از دست دادن بالقوه اطلاعات نوع خطا.

2 انواع خطای سفارشی

تعریف یک enum برای نشان دادن انواع خطا.

#[derive(Debug)]
enum MyError {
    EnvironmentVariableNotFound,
    IOError(std::io::Error),
}

impl From<std::env::VarError> for MyError {
    fn from(_: std::env::VarError) -> Self {
        Self::EnvironmentVariableNotFound
    }
}

impl From<std::io::Error> for MyError {
    fn from(value: std::io::Error) -> Self {
        Self::IOError(value)
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

منفی ها: Verbose اما کنترل خطای دقیقی را ارائه می دهد.

3. استفاده از thiserror

تعاریف خطای سفارشی را با حاشیه نویسی ساده می کند.

#[derive(thiserror::Error, Debug)]
enum MyError {
    #[error("Environment variable not found")]
    EnvironmentVariableNotFound(#[from] std::env::VarError),

    #[error(transparent)]
    IOError(#[from] std::io::Error),
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برای تعادل سادگی و کنترل بسیار توصیه می شود.

4 anyhow

هر نوع خطا را محاصره می کند و به هزینه عملکرد راحتی می دهد.

use anyhow::Result;

fn main() -> Result<()> {
    let html = render()?;
    println!("{}", html);
    Ok(())
}

fn render() -> Result<String> {
    let file = std::env::var("MARKDOWN")?;
    let source = read_to_string(file)?;
    Ok(source)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

افکار نهایی: فقط این کار را انجام دهید!

با این تکنیک ها ، اکنون مجهز به مدیریت خطا در زنگ زدگی هستید. این که آیا شما سادگی یا کنترل خوب را ترجیح می دهید ، مکانیسم های کنترل خطای Rust می توانند با نیازهای شما سازگار شوند.


ما Leapcell ، انتخاب برتر شما برای میزبانی پروژه های زنگ زدگی هستیم.

جهش

Leapcell بستر سرور نسل بعدی برای میزبانی وب ، کارهای ASYNC و REDIS است:

پشتیبانی چند زبانی

  • با node.js ، پایتون ، برو یا زنگ زدگی توسعه دهید.

پروژه های نامحدود را به صورت رایگان مستقر کنید

  • فقط برای استفاده پرداخت کنید – بدون درخواست ، بدون هزینه.

راندمان هزینه بی نظیر

  • پرداخت به عنوان شما بدون هیچ گونه هزینه بیکار.
  • مثال: 25 دلار از درخواست های 6.94M در زمان پاسخ متوسط ​​60ms پشتیبانی می کند.

تجربه توسعه دهنده ساده

  • UI بصری برای راه اندازی بی دردسر.
  • خطوط لوله CI/CD کاملاً خودکار و ادغام GITOPS.
  • معیارهای زمان واقعی و ورود به سیستم برای بینش های عملی.

مقیاس پذیری بی دردسر و عملکرد بالا

  • مقیاس خودکار برای رسیدگی به همزمانی بالا با سهولت.
  • صفر سربار عملیاتی – فقط روی ساختمان تمرکز کنید.

در اسناد بیشتر کاوش کنید!

Leapcell را امتحان کنید

ما را در X دنبال کنید: LeapCellHQ


در وبلاگ ما بخوانید

نوشته های مشابه

دیدگاهتان را بنویسید

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

دکمه بازگشت به بالا