Running Examples & Demos
Relevant source files
The following files were used as context for generating this wiki page:
The sicp-rust project provides a comprehensive suite of executable examples and demonstrations that showcase the implementation of "Structure and Interpretation of Computer Programs" (SICP) concepts using the Rust programming language. These demos serve to bridge the gap between Scheme’s flexible, garbage-collected environment and Rust’s strict ownership and type systems.
The examples cover fundamental computer science topics including generic arithmetic systems, mutable data modeling, infinite streams, and metalinguistic abstraction. Each demo is designed to be executed via cargo run --example <name>, providing immediate feedback on the porting strategies used to handle Rust-specific challenges like interior mutability and lazy evaluation.
Sources: README.md, rust-examples/chapter3/examples/streams_demo.rs:1-10, rust-examples/chapter4/examples/metacircular_evaluator.rs:1-7
Metacircular Evaluator Demo
The metacircular evaluator demo demonstrates a Lisp-style interpreter implemented in Rust. It showcases how to handle symbolic expressions, environment management, and function application using Rust's algebraic data types (ADTs) and persistent data structures.
Evaluation Workflow
The evaluator threads an Environment through evaluation calls, returning a tuple containing the resulting Value and the updated Environment. This functional state threading allows for definitions to persist across subsequent evaluations.
The demo specifically highlights the use of im::HashMap for $O(1)$ cloning of environments and the use of the Expr enum for exhaustive matching during evaluation.
Sources: rust-examples/chapter4/examples/metacircular_evaluator.rs:11-137, rust-examples/chapter4/examples/metacircular_evaluator.rs:139-145
Key Expressions Demonstrated
| Expression Type | Scheme Syntax Example | Rust Implementation Component |
|---|---|---|
| Arithmetic | (+ (* 2 3) 5) | Expr::Application |
| Definition | (define x 10) | Expr::Define |
| Lambda | (lambda (n) ...) | Expr::Lambda |
| Conditional | (if pred cons alt) | Expr::If |
| Derived | (let ((x 1)) x) | Expr::Let |
Sources: rust-examples/chapter4/examples/metacircular_evaluator.rs:17-124
Stream Processing & Lazy Evaluation
The streams demonstration highlights how Rust's Iterator trait natively supports the lazy evaluation concepts found in SICP's delay and force. This demo provides examples of infinite sequences and signal processing.
Infinite Sequences
The system implements several infinite streams as custom Rust iterators, including:
- IntegersFrom: Generates an unbounded sequence of integers.
- Fibonacci: Produces the Fibonacci sequence.
- PrimesOptimized: Implements the Sieve of Eratosthenes to generate prime numbers.
Signal Processing Applications
The streams demo includes complex mathematical applications such as solving differential equations (e.g., $\frac{dy}{dt} = y$) and approximating $\pi$ using the Euler transform for accelerated convergence.
// Solving dy/dt = y with y(0) = 1
let _solution: Vec<f64> = solve(|y| y, 1.0, 0.01)
.take(101)
.enumerate()
.filter(|(i, _)| i % 10 == 0)
.collect();
Sources: rust-examples/chapter3/examples/streams_demo.rs:154-184
Generic Arithmetic System
The generic operations demo (derived from Section 2.5) illustrates how Rust's trait system replaces the "dispatch on type" and apply-generic patterns used in Scheme.
Architecture of Generic Operations
The system uses a Number enum to wrap different numeric types and implements standard library traits like Add, Sub, Mul, and Div.
Type Coercion Strategy
To handle mixed-type operations (e.g., adding an Integer to a Complex number), the system employs a "Type Tower" strategy using Rust's From and Into traits to raise lower-level types to higher-level representations.
| Type | Level | Coercion Target |
|---|---|---|
| SchemeNumber | 1 | Rational |
| Rational | 2 | Complex |
| Complex | 3 | (Highest) |
Sources: rust-examples/chapter2/src/section_2_5.rs:188-216, rust-examples/chapter2/src/section_2_5.rs:226-234
Mutable Data & Circuit Simulation
The Section 3.3 summary details examples involving mutable data structures, including queues, tables, and a digital circuit simulator.
Digital Circuit Simulator Logic
The simulator uses an event-driven architecture centered around a time-ordered Agenda.
- Wires: Maintain signal states and a list of action callbacks.
- Gates: Components (Inverters, And, Or) that register actions on wires to propagate signal changes after a specific delay.
- Agenda: A scheduler that executes registered actions as simulated time progresses.
Sources: rust-examples/chapter3/SECTION_3_3_SUMMARY.md:43-52
Implementation Challenges
The implementation of constraints and circuits in Rust requires RefCell for interior mutability to allow circular references and bidirectional propagation, which SICP implements naturally with Scheme's mutable pairs.
Sources: rust-examples/chapter3/SECTION_3_3_SUMMARY.md:54-62
Execution Summary
| Demo Name | Command | Primary Focus |
|---|---|---|
| Streams Demo | cargo run --example streams_demo | Lazy evaluation, infinite sequences |
| Metacircular Evaluator | cargo run --example metacircular_evaluator | Lisp interpreter, environment management |
| Generic Operations | cargo test section_2_5 | Trait-based polymorphism, type coercion |
| Mutable Data | cargo test section_3_3 | Digital circuits, mutable lists, hash tables |
These examples collectively demonstrate that while Rust's safety guarantees introduce friction in circular or lazy structures, they provide explicit clarity on data ownership and state propagation that is often implicit in Scheme.
Sources: rust-examples/chapter3/examples/streams_demo.rs:9, rust-examples/chapter4/examples/metacircular_evaluator.rs:7, rust-examples/chapter3/SECTION_3_3_SUMMARY.md:68-71, rust-examples/chapter3/STREAMS_GUIDE.md:144-148