Exercise 1: RPN Calculator - Solution
Main Solution
#![allow(unused)] fn main() { pub fn rpn_calc(expr: &str) -> Result<i32, String> { let mut stack: Vec<i32> = Vec::new(); for token in expr.split_whitespace() { match token { "+" => { let b = stack.pop().ok_or("Stack underflow")?; let a = stack.pop().ok_or("Stack underflow")?; stack.push(a + b); } "-" => { let b = stack.pop().ok_or("Stack underflow")?; let a = stack.pop().ok_or("Stack underflow")?; stack.push(a - b); } "*" => { let b = stack.pop().ok_or("Stack underflow")?; let a = stack.pop().ok_or("Stack underflow")?; stack.push(a * b); } "/" => { let b = stack.pop().ok_or("Stack underflow")?; let a = stack.pop().ok_or("Stack underflow")?; if b == 0 { return Err("Division by zero".to_string()); } stack.push(a / b); } _ => { // Try to parse as number let num = token .parse::<i32>() .map_err(|_| format!("Invalid token: {}", token))?; stack.push(num); } } } // Should have exactly one value left if stack.len() == 1 { Ok(stack[0]) } else if stack.is_empty() { Err("Empty expression".to_string()) } else { Err(format!("Too many operands: {} left on stack", stack.len())) } } }
Alternative Solution: With Helper Function
#![allow(unused)] fn main() { pub fn rpn_calc(expr: &str) -> Result<i32, String> { let mut stack: Vec<i32> = Vec::new(); fn apply_op(stack: &mut Vec<i32>, op: fn(i32, i32) -> Result<i32, String>) -> Result<(), String> { let b = stack.pop().ok_or("Stack underflow")?; let a = stack.pop().ok_or("Stack underflow")?; stack.push(op(a, b)?); Ok(()) } for token in expr.split_whitespace() { match token { "+" => apply_op(&mut stack, |a, b| Ok(a + b))?, "-" => apply_op(&mut stack, |a, b| Ok(a - b))?, "*" => apply_op(&mut stack, |a, b| Ok(a * b))?, "/" => apply_op(&mut stack, |a, b| { if b == 0 { Err("Division by zero".to_string()) } else { Ok(a / b) } })?, _ => stack.push(token.parse().map_err(|_| format!("Invalid token: {}", token))?), } } match stack.len() { 0 => Err("Empty expression".to_string()), 1 => Ok(stack[0]), _ => Err(format!("Too many operands: {} left on stack", stack.len())), } } }
Explanation
Key points:
- Vec as stack: We use
Vec::push()
andVec::pop()
for stack operations - Result propagation: The
?
operator propagates errors early - Option to Result:
ok_or()
convertsOption<T>
frompop()
toResult<T, E>
- Parse with error mapping:
parse()
returnsResult
, we usemap_err()
to customize the error message - Order matters: When popping for operators like
-
and/
, we popb
first, thena
- Final validation: We ensure exactly one value remains on the stack
Common mistakes to avoid:
- Forgetting to check for division by zero
- Wrong order when popping operands (leads to reversed subtraction/division)
- Not validating final stack size
- Not handling empty input