تمرین: ارزیابی عبارت

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

نوع Box در اینجا یک اشاره‌گر هوشمند است و در ادامه دوره به طور مفصل مورد بررسی قرار خواهد گرفت. یک عبارت می‌تواند با استفاده از Box::new "باکس" شود، همان‌طور که در تست‌ها مشاهده می‌شود. برای ارزیابی یک عبارت باکس‌شده، از عملگر deref (*) برای "باز کردن باکس" استفاده کنید: eval(*boxed_expr).

برخی از عبارات نمی‌توانند ارزیابی شوند و خطا برمی‌گردانند. نوع استاندارد Result<Value, String> یک enum است که یا نمایانگر یک مقدار موفقیت‌آمیز (Ok(Value)) یا یک خطا (Err(String)) است. ما این نوع را به‌طور مفصل‌تر در آینده پوشش خواهیم داد.

کد را کپی و در Rust Playground پیست کنید و پیاده‌سازی تابع eval را آغاز کنید. محصول نهایی باید تست‌ها را پاس کند. ممکن است استفاده از ()!todo و گذراندن تست‌ها به صورت تک به تک مفید باشد. همچنین می‌توانید به طور موقت یک تست را با استفاده از [ignore]# نادیده بگیرید:

#[test]
#[ignore]
fn test_value() { .. }

اگر زودتر تمام کردید، سعی کنید یک تست بنویسید که منجر به تقسیم بر صفر یا سرریز عدد صحیح شود. چگونه می‌توانید این را با استفاده از Result به جای panic مدیریت کنید؟

#![allow(unused)]
fn main() {
/// An operation to perform on two subexpressions.
#[derive(Debug)]
enum Operation {
    Add,
    Sub,
    Mul,
    Div,
}

/// An expression, in tree form.
#[derive(Debug)]
enum Expression {
    /// An operation on two subexpressions.
    Op { op: Operation, left: Box<Expression>, right: Box<Expression> },

    /// A literal value
    Value(i64),
}

fn eval(e: Expression) -> Result<i64, String> {
    todo!()
}

#[test]
fn test_value() {
    assert_eq!(eval(Expression::Value(19)), Ok(19));
}

#[test]
fn test_sum() {
    assert_eq!(
        eval(Expression::Op {
            op: Operation::Add,
            left: Box::new(Expression::Value(10)),
            right: Box::new(Expression::Value(20)),
        }),
        Ok(30)
    );
}

#[test]
fn test_recursion() {
    let term1 = Expression::Op {
        op: Operation::Mul,
        left: Box::new(Expression::Value(10)),
        right: Box::new(Expression::Value(9)),
    };
    let term2 = Expression::Op {
        op: Operation::Mul,
        left: Box::new(Expression::Op {
            op: Operation::Sub,
            left: Box::new(Expression::Value(3)),
            right: Box::new(Expression::Value(4)),
        }),
        right: Box::new(Expression::Value(5)),
    };
    assert_eq!(
        eval(Expression::Op {
            op: Operation::Add,
            left: Box::new(term1),
            right: Box::new(term2),
        }),
        Ok(85)
    );
}

#[test]
fn test_error() {
    assert_eq!(
        eval(Expression::Op {
            op: Operation::Div,
            left: Box::new(Expression::Value(99)),
            right: Box::new(Expression::Value(0)),
        }),
        Err(String::from("تقسیم بر صفر"))
    );
}
}