mirror of
https://github.com/google/comprehensive-rust.git
synced 2024-12-15 22:37:28 +02:00
Update expression-evaluation exercise: more patterns, more enums (#1582)
This modifies the exercise to lean more into interesting `match` statements. It also uses the standard `Result` type, based on feedback that students could understand it sufficiently at this point in the course. Addresses #1565.
This commit is contained in:
parent
302a03bbe3
commit
085cbf2c1e
@ -4,31 +4,18 @@ minutes: 30
|
||||
|
||||
# Exercise: Expression Evaluation
|
||||
|
||||
Let's write a simple recursive evaluator for arithmetic expressions. Start with
|
||||
an enum defining the binary operations:
|
||||
|
||||
```rust
|
||||
{{#include exercise.rs:Operation}}
|
||||
|
||||
{{#include exercise.rs:Expression}}
|
||||
|
||||
{{#include exercise.rs:Res}}
|
||||
|
||||
{{#include exercise.rs:eval}}
|
||||
todo!()
|
||||
}
|
||||
|
||||
{{#include exercise.rs:tests}}
|
||||
```
|
||||
Let's write a simple recursive evaluator for arithmetic expressions.
|
||||
|
||||
The `Box` type here is a smart pointer, and will be covered in detail later in
|
||||
the course. An expression can be "boxed" with `Box::new` as seen in the tests.
|
||||
To evaluate a boxed expression, use the deref operator to "unbox" it:
|
||||
To evaluate a boxed expression, use the deref operator (`*`) to "unbox" it:
|
||||
`eval(*boxed_expr)`.
|
||||
|
||||
Some expressions cannot be evaluated and will return an error. The `Res`
|
||||
type represents either a successful value or an error with a message. This is
|
||||
very similar to the standard-library `Result` which we will see later.
|
||||
Some expressions cannot be evaluated and will return an error. The standard
|
||||
[`Result<Value,
|
||||
String>`](https://doc.rust-lang.org/std/result/enum.Result.html) type is an
|
||||
enum that represents either a successful value (`Ok(Value)`) or an error
|
||||
(`Err(String)`). We will cover this type in detail later.
|
||||
|
||||
Copy and paste the code into the Rust playground, and begin implementing
|
||||
`eval`. The final product should pass the tests. It may be helpful to use
|
||||
@ -42,5 +29,18 @@ temporarily with
|
||||
fn test_value() { .. }
|
||||
```
|
||||
|
||||
If you finish early, try writing a test that results in an integer overflow.
|
||||
How could you handle this with `Res::Err` instead of a panic?
|
||||
If you finish early, try writing a test that results in division by zero or
|
||||
integer overflow. How could you handle this with `Result` instead of a panic?
|
||||
|
||||
```rust
|
||||
{{#include exercise.rs:Operation}}
|
||||
|
||||
{{#include exercise.rs:Expression}}
|
||||
|
||||
{{#include exercise.rs:eval}}
|
||||
todo!()
|
||||
}
|
||||
|
||||
{{#include exercise.rs:tests}}
|
||||
```
|
||||
|
||||
|
@ -41,31 +41,18 @@ enum Expression {
|
||||
}
|
||||
// ANCHOR_END: Expression
|
||||
|
||||
// ANCHOR: Res
|
||||
/// The result of evaluating an expression.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Res {
|
||||
/// Evaluation was successful, with the given result.
|
||||
Ok(i64),
|
||||
/// Evaluation failed, with the given error message.
|
||||
Err(String),
|
||||
}
|
||||
// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.
|
||||
use Res::{Err, Ok};
|
||||
// ANCHOR_END: Res
|
||||
|
||||
// ANCHOR: eval
|
||||
fn eval(e: Expression) -> Res {
|
||||
fn eval(e: Expression) -> Result<i64, String> {
|
||||
// ANCHOR_END: eval
|
||||
match e {
|
||||
Expression::Op { op, left, right } => {
|
||||
let left = match eval(*left) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(msg),
|
||||
e @ Err(_) => return e,
|
||||
};
|
||||
let right = match eval(*right) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return Err(msg),
|
||||
e @ Err(_) => return e,
|
||||
};
|
||||
Ok(match op {
|
||||
Operation::Add => left + right,
|
||||
|
Loading…
Reference in New Issue
Block a user