@@ -0,0 +1,2 @@ | |||
/target | |||
**/*.rs.bk |
@@ -0,0 +1,178 @@ | |||
[[package]] | |||
name = "autocfg" | |||
version = "0.1.2" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "bitflags" | |||
version = "1.0.4" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "cloudabi" | |||
version = "0.0.3" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "fuchsia-cprng" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "hw4" | |||
version = "0.1.0" | |||
dependencies = [ | |||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "libc" | |||
version = "0.2.50" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "rand" | |||
version = "0.6.5" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_chacha" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_core" | |||
version = "0.3.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_core" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "rand_hc" | |||
version = "0.1.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_isaac" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_jitter" | |||
version = "0.1.3" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_os" | |||
version = "0.1.3" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_pcg" | |||
version = "0.1.2" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_xorshift" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rdrand" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "winapi" | |||
version = "0.3.6" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "winapi-i686-pc-windows-gnu" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "winapi-x86_64-pc-windows-gnu" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[metadata] | |||
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" | |||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" | |||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" | |||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" | |||
"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" | |||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" | |||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" | |||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" | |||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" | |||
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" | |||
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" | |||
"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" | |||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" | |||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" | |||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" | |||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" | |||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" | |||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | |||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
@@ -0,0 +1,8 @@ | |||
[package] | |||
name = "hw4" | |||
version = "0.1.0" | |||
authors = ["CS 538"] | |||
edition = "2018" | |||
[dependencies] | |||
rand = "0.6.5" |
@@ -0,0 +1,51 @@ | |||
# Reverse Polish Notation Calculator | |||
Because it's important to write a calculator in every language, we will produce | |||
a small Reverse Polish Notation calculator in Rust. Here is an intro on RPN: | |||
https://en.wikipedia.org/wiki/Reverse_Polish_notation | |||
The rough idea is that a RPN calculator is a *stack language*: we push things | |||
onto a stack as we parse them, and when we parse operations we pop off the | |||
required number of arguments, apply the operation, and push the result back on | |||
to the stack. | |||
We've supplied much of the code to help you get started. There are two main | |||
files (and two main tasks): `rpn.rs` and `parser.rs`. | |||
## rpn.rs | |||
In this file, you will implement the main data structure for our calculator and | |||
give the evaluation functions. The main data structure is a Stack; you are free | |||
to implement the functions however you like, but a Vector might be a good | |||
choice... (note that Vectors have `push` and `pop` operations). | |||
You will also implement the evaluation function: given a Stack and an operation, | |||
perform the indicated operation on the Stack. Your calculator should support (at | |||
least) the following operations: | |||
* Add (`+`): Add two numbers together. Sample input: `3 5 +` should lead to 8. | |||
* Eq (`=`): Check if two numbers or two booleans are equal. Sample input: `3 5 =` should lead to false. | |||
* Neg (`~`): Negate a boolean. Sample input: `false ~` should lead to true. | |||
* Swap (`<->`): Swap the top two elements of the stack. Sample input: `0 1 <->` should lead to `1 0`. | |||
* Rand (`#`): Produce a random integer from 0 to the top element of the stack. Sample input: `5 #` should lead to a uniform integer in {0, 1, 2, 3, 4} | |||
* Cond (`?`): If-then-else. Looks at the top three elements of the stack, and | |||
does an if-then-else. Sample input: `true 1 2 ?` should lead to 1, and `false 1 2 ?` should lead to 2. | |||
* Quit (`quit`): Quit the calculator. | |||
Your calculator should also accept numbers and boolean constants: `true` and `false`. | |||
To implement the Rand operation, we will use the `rand` crate. Taking a quick | |||
look at the basic examples should be more than enough: | |||
https://docs.rs/rand/0.6.5/rand/ | |||
We've already added the `rand` dependency to the Cargo.toml file; take a look if | |||
you're curious. | |||
## parser.rs | |||
This file contains the parser and main read-eval-print loop. We'll be relying on | |||
standard libraries for parsing. You will write the main loop of the evaluator, | |||
which will take in a token and update the stack/apply the indicated operation. | |||
You shouldn't need a lot of code here---the hard work is in the other file. |
@@ -0,0 +1,16 @@ | |||
#![allow(dead_code)] | |||
#![forbid(unsafe_code)] | |||
mod problem1; | |||
mod problem2; | |||
mod problem3; | |||
mod problem4; | |||
mod parser; | |||
mod rpn; | |||
fn main() { | |||
if let Err(err) = parser::rpn_repl() { | |||
println!("Error: {:?}", err); | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
/* | |||
* CS 538, Spring 2019: HW4 | |||
* | |||
* Reverse Polish Notation: parser.rs | |||
* See `rpn.md` for the overview. | |||
*/ | |||
extern crate rand; | |||
use std::io::{self, Write}; | |||
use super::rpn; | |||
pub fn rpn_repl() -> rpn::Result<()> { | |||
let mut stack = rpn::Stack::new(); | |||
let mut input = String::new(); | |||
// Read-eval-print loop | |||
loop { | |||
// Clear the input buffer | |||
input.clear(); | |||
// Prompt the user | |||
print!("> "); | |||
io::stdout().flush().map_err(rpn::Error::IO)?; | |||
// Read a line and evaluate it | |||
io::stdin().read_line(&mut input).map_err(rpn::Error::IO)?; | |||
evaluate_line(&mut stack, &input)?; | |||
// A successful run should end with a stack with a exactly one item: the result | |||
let res = stack.pop()?; | |||
if stack.empty() { | |||
println!("Reply> {:?}", res); | |||
} else { | |||
return Err(rpn::Error::Extra); | |||
} | |||
} | |||
} | |||
fn evaluate_line(stack: &mut rpn::Stack, buf: &String) -> rpn::Result<()> { | |||
// println!("Evaluating Input: {}", buf); | |||
// Trim whitespace and split; this gives an iterator of tokens. | |||
let tokens = buf.trim().split_whitespace(); | |||
/* | |||
* Write the main loop processing the tokens. The `parse` method for Strings will be useful for | |||
* parsing integers. See here for examples: | |||
* | |||
* https://doc.rust-lang.org/std/primitive.str.html#method.parse | |||
*/ | |||
for tok in tokens { | |||
// println!("Current Tok: {}", tok); | |||
match tok { | |||
"quit" => { | |||
stack.eval(rpn::Op::Quit)?; | |||
} | |||
"+" => { | |||
stack.eval(rpn::Op::Add)?; | |||
} | |||
"=" => { | |||
stack.eval(rpn::Op::Eq)?; | |||
} | |||
"~" => { | |||
stack.eval(rpn::Op::Neg)?; | |||
} | |||
"<->" => { | |||
stack.eval(rpn::Op::Swap)?; | |||
} | |||
"#" => { | |||
stack.eval(rpn::Op::Rand)?; | |||
} | |||
"?" => { | |||
stack.eval(rpn::Op::Cond)?; | |||
} | |||
"true" => { | |||
stack.push(rpn::Item::Bool(true))?; | |||
} | |||
"false" => { | |||
stack.push(rpn::Item::Bool(false))?; | |||
} | |||
_ => { | |||
match tok.parse::<i32>() { | |||
Ok(x) => { | |||
// println!("Pushed one int {}", x); | |||
stack.push(rpn::Item::Int(x))?; | |||
} | |||
Err(_e) => return Err(rpn::Error::Syntax), | |||
} | |||
} | |||
} | |||
} | |||
Ok(()) | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* CS 538, Spring 2019: HW4 | |||
* Problem 1 | |||
* | |||
* Write implementations of the following simple functions. You can use basic Vector operations | |||
* like `push`, `pop`, and `contains`, but do not use the built-in `dedup` or `filter` functions, | |||
* for instance. | |||
* | |||
* Take a look at the Vector documentation for a good intro to Rust Vectors: | |||
* | |||
* https://doc.rust-lang.org/std/vec/struct.Vec.html | |||
*/ | |||
/// Compute the sum of a vector of integers | |||
pub fn sum(vs: &Vec<i32>) -> i32 { | |||
let mut sum = 0; | |||
for x in vs { | |||
sum += x; | |||
} | |||
sum | |||
} | |||
/// Return a copy of the input vector, keeping only the first copy of each element. | |||
pub fn dedup(vs: &Vec<i32>) -> Vec<i32> { | |||
let mut res = Vec::new(); | |||
for x in vs { | |||
if !res.contains(x) { | |||
res.push(*x); | |||
} | |||
} | |||
res | |||
} | |||
/// Return a copy of the input vector keeping only elements where the predicate is true. The order | |||
/// of elements should not be changed. | |||
pub fn filter(vs: &Vec<i32>, pred: &Fn(i32) -> bool) -> Vec<i32> { | |||
// unimplemented!() | |||
let mut res = Vec::new(); | |||
for x in vs { | |||
if pred(*x) { | |||
res.push(*x); | |||
} | |||
} | |||
res | |||
} | |||
/// You can put more tests here. | |||
#[cfg(test)] | |||
mod tests { | |||
use super::*; | |||
#[test] | |||
fn test_sum() { | |||
let vs = vec![1, 2, 3, 4, 5]; | |||
assert_eq!(sum(&vs), 15); | |||
} | |||
#[test] | |||
fn test_dedup() { | |||
let vs = vec![5, 4, 3, 2, 1, 2, 3, 4, 5]; | |||
assert_eq!(dedup(&vs), [5, 4, 3, 2, 1]); | |||
} | |||
#[test] | |||
fn test_filter() { | |||
let vs = vec![5, 4, 3, 2, 1, 2, 3, 4, 5]; | |||
assert_eq!(filter(&vs, &|i: i32| i % 2 == 1), [5, 3, 1, 3, 5]); | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
/* | |||
* CS 538, Spring 2019: HW4 | |||
* Problem 2 | |||
* | |||
* We've defined a type alias for vectors of vectors, which we will use to represent matrices of | |||
* floats. Write a function to multiply two matrices; we've supplied the type signature already. | |||
* Our reference solution assumes that Matrix is a vector of vectors, where each inner vector is a | |||
* row of the matrix. For instance, `mat[1][2]` is the element in the second row, third column | |||
* (remember that indices start at 0). You should follow this representation. | |||
* | |||
* For a brief refresher on matrix multiplication: | |||
* | |||
* https://en.wikipedia.org/wiki/Matrix_multiplication#Definition | |||
* | |||
* Note that not all pairs of matrices can be multiplied: if we are multiplying matrices A * B, the | |||
* number of columns in A must be equal to the number of rows in B. Your program is should panic | |||
* (whether from `panic!`, `assert!`, `assert_eq!`, out of bounds, or whatever) if this condition | |||
* is not satisfied, though in a more polished implementation we would try to handle these errors | |||
* more gracefully. | |||
* | |||
* Also note that indexing with square brackets in Rust is checked. If you access something beyond | |||
* the end of the vector, your program will panic. Don't do this! | |||
*/ | |||
pub type Matrix = Vec<Vec<i32>>; | |||
/// Multiply two matrices and return the result in a new matrix | |||
pub fn mat_mult(mat1: &Matrix, mat2: &Matrix) -> Matrix { | |||
assert!(mat1.len() > 0); | |||
assert!(mat2.len() > 0); | |||
assert_eq!(mat1[0].len(), mat2.len()); | |||
let mut res: Matrix = vec![vec![0; mat2[0].len()]; mat1.len()]; | |||
for i in 0..mat1.len() { | |||
for j in 0..mat2[0].len() { | |||
for k in 0..mat2.len() { | |||
res[i][j] += mat1[i][k] * mat2[k][j]; | |||
} | |||
} | |||
} | |||
res | |||
} | |||
/// You can put more tests here. | |||
#[cfg(test)] | |||
mod tests { | |||
use super::*; | |||
#[test] | |||
fn test_mat_mult() { | |||
let mat1 = vec![vec![2, 3], vec![1, 4]]; | |||
let mat2 = vec![vec![7, 3], vec![1, 9]]; | |||
let mat3 = vec![vec![3, 23], vec![5, 71]]; | |||
let mat4 = vec![vec![7, 18], vec![6, 19]]; | |||
let mat5 = vec![vec![1], vec![2]]; | |||
let mat6 = vec![vec![8], vec![9]]; | |||
assert_eq!(&mat_mult(&mat1, &mat1), &mat4); | |||
assert_eq!(&mat_mult(&mat1, &mat5), &mat6); | |||
assert_eq!(mat_mult(&mat1, &mat2), mat_mult(&mat2, &mat1)); | |||
assert_eq!( | |||
mat_mult(&mat_mult(&mat1, &mat2), &mat3), | |||
mat_mult(&mat1, &mat_mult(&mat2, &mat3)) | |||
); | |||
} | |||
#[test] | |||
#[should_panic] | |||
fn test_mat_mult_bad() { | |||
let mat1 = vec![vec![2, 3], vec![1, 4]]; | |||
let mat2 = vec![vec![1], vec![2]]; | |||
mat_mult(&mat2, &mat1); | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* CS 538, Spring 2019: HW4 | |||
* Problem 3 | |||
* | |||
* Write a function that takes in an integer `n`, and returns a vector with all of the prime | |||
* numbers up to `n`. There are many ways to do this; one way is the Sieve of Eratothenes: | |||
* | |||
* https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Overview | |||
* | |||
* But you can choose any way you like. | |||
*/ | |||
/// Return the list of all prime numbers up to and including n. | |||
pub fn sieve(n: u32) -> Vec<u32> { | |||
let sz = n as usize; | |||
let mut bprime = vec![true; sz + 1]; | |||
bprime[0] = false; | |||
if n >= 1 { | |||
bprime[1] = false; | |||
} | |||
for i in 2..sz + 1 { | |||
if bprime[i] { | |||
let mut sq = i * i; | |||
while sq <= sz { | |||
bprime[sq] = false; | |||
sq += i; | |||
} | |||
} | |||
} | |||
let mut res: Vec<u32> = Vec::new(); | |||
for (i, x) in bprime.iter().enumerate() { | |||
if *x { | |||
res.push(i as u32) | |||
} | |||
} | |||
res | |||
} | |||
/// You can put more tests here. | |||
#[cfg(test)] | |||
mod tests { | |||
use super::*; | |||
#[test] | |||
fn test_sieve() { | |||
let primes_twelve = vec![2, 3, 5, 7, 11]; | |||
assert_eq!(sieve(12), primes_twelve); | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* CS 538, Spring 2019: HW4 | |||
* Problem 4 | |||
* | |||
* You will write a small Towers of Hanoi solver. This is a classical puzzle, involving moving a | |||
* set of differently-sized discs from one pole to another pole, while making sure that smaller | |||
* discs sit on top larger discs at all time. See here for some animations: | |||
* | |||
* https://en.wikipedia.org/wiki/Tower_of_Hanoi | |||
* | |||
* We first make a type of pegs: there are three possible pegs, called A, B, C | |||
*/ | |||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | |||
pub enum Peg { | |||
A, | |||
B, | |||
C, | |||
} | |||
/* | |||
* A move from peg1 to peg2 is represented by a pair (peg1, peg2). | |||
*/ | |||
pub type Move = (Peg, Peg); | |||
/* | |||
* Write a program that takes the number of discs (`num_discs`) and three pegs (`src`, `aux`, | |||
* `dst`) and outputs a vector of moves to move `num_discs` discs from `src` to `dst`, using the | |||
* `aux` peg. You don't need to come up with the algorithm. You can implement the one here: | |||
* | |||
* https://en.wikipedia.org/wiki/Tower_of_Hanoi#Simpler_statement_of_iterative_solution | |||
* | |||
* You may assume that `src`, `aux`, and `dst` are all different. | |||
* You may want to write some helper functions to avoid repeating lots of code. | |||
*/ | |||
// Helper function for performing a more concise recursive solution | |||
fn hanoi_move(n: u32, src: Peg, dst: Peg, aux: Peg, res: &mut Vec<Move>) { | |||
if n > 0 { | |||
hanoi_move(n - 1, src, aux, dst, res); | |||
res.push((src, dst)); | |||
hanoi_move(n - 1, aux, dst, src, res); | |||
} | |||
} | |||
pub fn hanoi(num_discs: u32, src: Peg, aux: Peg, dst: Peg) -> Vec<Move> { | |||
let mut res: Vec<Move> = Vec::new(); | |||
hanoi_move(num_discs, src, dst, aux, &mut res); | |||
res | |||
} | |||
/// You can put more tests here. | |||
#[cfg(test)] | |||
mod tests { | |||
use super::*; | |||
#[test] | |||
fn test_hanoi() { | |||
let moves = vec![ | |||
(Peg::A, Peg::C), | |||
(Peg::A, Peg::B), | |||
(Peg::C, Peg::B), | |||
(Peg::A, Peg::C), | |||
(Peg::B, Peg::A), | |||
(Peg::B, Peg::C), | |||
(Peg::A, Peg::C), | |||
]; | |||
assert_eq!(hanoi(3, Peg::A, Peg::B, Peg::C), moves); | |||
} | |||
} |
@@ -0,0 +1,195 @@ | |||
/* | |||
* CS 538, Spring 2019: HW4 | |||
* | |||
* Reverse Polish Notation: rpn.rs | |||
* See `rpn.md` for the overview. | |||
*/ | |||
use rand::prelude::*; | |||
use std::collections::LinkedList; | |||
use std::io; | |||
// Stacks will work with Items, which either either integers or booleans | |||
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug)] | |||
pub enum Item { | |||
Int(i32), | |||
Bool(bool), | |||
} | |||
// List of possible errors | |||
#[derive(Debug)] | |||
pub enum Error { | |||
Empty, // Tried to pop empty stack | |||
Extra, // Stack ended with extra elements | |||
Type, // Type mismatch | |||
Syntax, // Syntax error, didn't recognize op | |||
IO(io::Error), // Some kind of IO error | |||
Quit, // User quitting | |||
} | |||
// Base operations supported by calculator, see rpn.md | |||
#[derive(Debug)] | |||
pub enum Op { | |||
Add, | |||
Eq, | |||
Neg, | |||
Swap, | |||
Rand, | |||
Cond, | |||
Quit, | |||
} | |||
// We'll define a result type for our calculator: either a valid value, or a calculator Error | |||
pub type Result<T> = std::result::Result<T, Error>; | |||
// Define a type for Stacks | |||
#[derive(Debug)] | |||
pub struct Stack { | |||
ll: LinkedList<Item>, | |||
} | |||
// Implement the following functions on Stacks | |||
impl Stack { | |||
// Make a new Stack | |||
pub fn new() -> Stack { | |||
Stack { | |||
ll: LinkedList::new(), | |||
} | |||
} | |||
// Check if a Stack is empty | |||
pub fn empty(&self) -> bool { | |||
self.ll.is_empty() | |||
} | |||
// Push an item onto a stack (should never error) | |||
pub fn push(&mut self, item: Item) -> Result<()> { | |||
self.ll.push_back(item); | |||
Ok(()) | |||
} | |||
// Pop an item off the Stack; may result in Empty error | |||
pub fn pop(&mut self) -> Result<Item> { | |||
let res = self.ll.pop_back(); | |||
match res { | |||
Some(x) => Ok(x), | |||
None => Err(Error::Empty), | |||
} | |||
} | |||
/* | |||
* Main evaluation function: apply an operation to a Stack | |||
* | |||
* Hint: Rust has a nice bit of syntax when writing functions returning Result: | |||
* | |||
* ``` | |||
* let x = foo()?; | |||
* ``` | |||
* | |||
* If `foo()` returns `Ok(ret)`, `ret` will be assigned to `x`. Otherwise if `foo()` returns | |||
* `Err(e)`, the assignment is not performed and `Err(e)` is returned from the enclosing | |||
* function. | |||
* | |||
* This syntax makes it easier to work with multiple operations that may fail, like: | |||
* | |||
* ``` | |||
* let x = foo()?; | |||
* let y = bar(x)?; | |||
* let z = baz(x, y)?; | |||
* ... | |||
* ``` | |||
* | |||
* The first one to fail will abort the whole function while returning the error. | |||
*/ | |||
pub fn eval(&mut self, op: Op) -> Result<()> { | |||
match op { | |||
Op::Add => { | |||
let x = self.pop()?; | |||
let y = self.pop()?; | |||
if let Item::Int(xx) = x { | |||
if let Item::Int(yy) = y { | |||
self.push(Item::Int(xx + yy))?; | |||
Ok(()) | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} | |||
Op::Eq => { | |||
let x = self.pop()?; | |||
let y = self.pop()?; | |||
if let Item::Int(xx) = x { | |||
if let Item::Int(yy) = y { | |||
self.push(Item::Bool(xx == yy))?; | |||
Ok(()) | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} else if let Item::Bool(xx) = x { | |||
if let Item::Bool(yy) = y { | |||
self.push(Item::Bool(xx == yy))?; | |||
Ok(()) | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} | |||
Op::Neg => { | |||
let x = self.pop()?; | |||
if let Item::Bool(xx) = x { | |||
self.push(Item::Bool(!xx))?; | |||
Ok(()) | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} | |||
Op::Swap => { | |||
let x = self.pop()?; | |||
let y = self.pop()?; | |||
self.push(x)?; | |||
self.push(y)?; | |||
Ok(()) | |||
} | |||
Op::Rand => { | |||
let x = self.pop()?; | |||
if let Item::Int(xx) = x { | |||
if xx >= 0 { | |||
let mut rng = rand::thread_rng(); | |||
self.push(Item::Int(rng.gen_range(0, xx)))?; | |||
Ok(()) | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} | |||
Op::Cond => { | |||
let y = self.pop()?; | |||
let x = self.pop()?; | |||
let b = self.pop()?; | |||
if let Item::Bool(bb) = b { | |||
if bb { | |||
self.push(x)?; | |||
} else { | |||
self.push(y)?; | |||
} | |||
Ok(()) | |||
} else { | |||
Err(Error::Type) | |||
} | |||
} | |||
Op::Quit => { | |||
// if !self.empty() { | |||
// return Err(Error::Extra); | |||
// } | |||
// Main.rs has that check, supressing here | |||
Ok(()) | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,2 @@ | |||
/target | |||
**/*.rs.bk |
@@ -0,0 +1,178 @@ | |||
[[package]] | |||
name = "autocfg" | |||
version = "0.1.2" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "bitflags" | |||
version = "1.0.4" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "cloudabi" | |||
version = "0.0.3" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "fuchsia-cprng" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "hw5" | |||
version = "0.1.0" | |||
dependencies = [ | |||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "libc" | |||
version = "0.2.51" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "rand" | |||
version = "0.6.5" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_chacha" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_core" | |||
version = "0.3.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_core" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "rand_hc" | |||
version = "0.1.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_isaac" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_jitter" | |||
version = "0.1.3" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_os" | |||
version = "0.1.3" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_pcg" | |||
version = "0.1.2" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rand_xorshift" | |||
version = "0.1.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "rdrand" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "winapi" | |||
version = "0.3.7" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
name = "winapi-i686-pc-windows-gnu" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[[package]] | |||
name = "winapi-x86_64-pc-windows-gnu" | |||
version = "0.4.0" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
[metadata] | |||
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" | |||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" | |||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" | |||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" | |||
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" | |||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" | |||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" | |||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" | |||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" | |||
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" | |||
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" | |||
"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" | |||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" | |||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" | |||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" | |||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" | |||
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" | |||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | |||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
@@ -0,0 +1,8 @@ | |||
[package] | |||
name = "hw5" | |||
version = "0.1.0" | |||
authors = ["CS 538"] | |||
edition = "2018" | |||
[dependencies] | |||
rand = "0.6.5" |
@@ -0,0 +1,203 @@ | |||
# Binary Search Trees | |||
In this assignment, you will design key-value maps based on binary search trees | |||
in Rust. If you need a refresher about how these datastructures work: | |||
https://en.wikipedia.org/wiki/Binary_search_tree | |||
In particular, all of your operations should maintain the central BST invariant: | |||
from any node, all of the keys in the left subtree are less than the key of the | |||
parent, and all of the keys in the right subtree are greater than the key of the | |||
parent. Furthermore, all keys in a BST must be unique---no repetitions. | |||
There are many variants on BSTs that achieve better balancing, like red-black | |||
trees, AVL trees, B-Trees, etc. We won't worry about that too much---our goal is | |||
just a basic BST. We'll already have our hands full with the Rust compiler. | |||
## TreeMap functions | |||
Implement the TreeMap methods in `bst.rs` without changing the function types. | |||
You may use recursion in all functions (but see the extensions section). | |||
## Iterators for TreeMap | |||
In the second part of the assignment, you will design three kinds of iterators | |||
for TreeMaps: | |||
1. Consuming iterator (typically created with `.into_iter()`) | |||
2. Borrowing iterator (typically created with `.iter()`) | |||
3. Mutable iterator (typically created with `.iter_mut()`) | |||
The first iterator consumes the whole TreeMap, returning owned data. The second | |||
iterator "borrows" TreeMap, returning immutable references to data ("borrowed | |||
values"). The third iterator returns *mutable* references to data, so that the | |||
client can update the TreeMap in place. To make sure that the client doesn't | |||
mess up the BST property, we will give out *immutable* references to keys and | |||
*mutable* references to values for this last iterator. The other iterators | |||
should give (key, value) pairs. | |||
If you look for these functions for Rust's BTreeMap's, you'll see that the | |||
standard library implements fancier iterators. For instance, the standard | |||
library iterators are double-ended so you can get elements from the front or | |||
elements from the back. We'll just implement regular iterators. | |||
## Don't panic | |||
Some standard functions in Rust may panic. The function `unwrap()` is one main | |||
example. Avoid using these functions unless you really know that they will not | |||
panic, for instance if you have already checked that the Option is not None. | |||
There are usually better ways of getting values out from Option, like pattern | |||
matching, | |||
[if-let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html), | |||
and | |||
[while-let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html). | |||
## Debugging | |||
For convenience, we've added the Debug trait bound everywhere so you can print | |||
out values. A real version of the library would not have these bounds (since | |||
TreeMaps should work on all types, not just ones with the Debug trait). You're | |||
free to strip the Debug traits when you're done---we won't be testing them. | |||
Rust 1.32 (landed mid January) has a new `dbg!` macro that is quite nice. See | |||
examples [here](https://doc.rust-lang.org/std/macro.dbg.html). Or, you can use | |||
regular `println!` or `eprintln!` with a special formatting token: | |||
``` | |||
println!("What is this value: {:?}", thing_with_debug); | |||
``` | |||
## Tips | |||
There's no way around it: you're going to meet the borrow checker. After working | |||
at it, you'll get a better idea of what the errors mean as you improve your | |||
mental model of how Rust "works", how the compiler reasons about your program. | |||
You'll want to read the docs carefully (and examples), especially for the | |||
following parts of the stdlib: | |||
* Option: https://doc.rust-lang.org/std/option/index.html | |||
You'll be using the `take` function a lot. The `std::mem::swap` and | |||
`std::mem::replace` functions may also be helpful. | |||
* Box: https://doc.rust-lang.org/std/boxed/index.html | |||
Box is a basic example of a *smart pointer*, something that looks and feels like | |||
a regular reference, but with some special features. The basic idea is that a | |||
Box points to some data located on the heap. Unlike standard references, the Box | |||
owns the data it points to. We use Boxes to define inductive datastructures like | |||
TreeMap, where the total size of the data is not known at compile time. | |||
For now, just treat Box<T> like a regular reference &T or &mut T, depending on | |||
whether the Box is mutable or not. To make a new Box holding something, use the | |||
constructor `Box::new(the_thing)`. If you have a Box `b`, you can dereference it | |||
to get the thing that it's pointing at by doing `*b`. Just like for references, | |||
you can call a method of the boxed thing via `b.foo()`. | |||
These may look like magic, but as we will discuss this behavior and syntax is | |||
enabled by Rust traits (`Deref`). Rust also has some "magic" for Box, special | |||
syntax for this type. For instance, `*b` gives ownership of the boxed thing | |||
whereas usually `*p` only gets a reference to the data. | |||
* BTreeMap: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html | |||
Our API is based off of this collection, though the stdlib implementation is | |||
much fancier (and much faster). Incidentally, the collections module has great | |||
documentation: | |||
https://doc.rust-lang.org/std/collections/index.html | |||
* Indexing traits | |||
These are the standard Rust traits for allowing indexing, things like `map[42]`. | |||
https://doc.rust-lang.org/std/ops/trait.Index.html | |||
https://doc.rust-lang.org/std/ops/trait.IndexMut.html | |||
* Iterator trait: https://doc.rust-lang.org/std/iter/trait.Iterator.html | |||
You need two ingredients to implement this trait: a type of items to return (an | |||
"associated type") and a `next` function, which returns the next item and | |||
updates the iterator state. | |||
* Associated iterator traits: FromIterator and IntoIterator | |||
https://doc.rust-lang.org/std/iter/trait.FromIterator.html | |||
https://doc.rust-lang.org/std/iter/trait.IntoIterator.html | |||
These are the standard traits for converting to/from iterators, e.g., via | |||
`.collect()`. | |||
These are the standard traits for working with range-like things, for instance | |||
stuff created by `(1..10)` or `('a'..'z')`. Note that there is no requirement | |||
that the range elements are ordered (i.e., implement Ord), but we are working | |||
with ranges of keys and keys must be ordered. | |||
Parts of this assignment are inspired by Learning Rust with Entirely Too Many | |||
Linked Lists (TMLL): | |||
https://cglab.ca/~abeinges/blah/too-many-lists/book/README.html | |||
The material in 2-3 cover similar ground to what we are doing, but for boring | |||
linked lists instead of amazingly cool BSTs. Keep in mind that TMLL was written | |||
a few years ago, and the Rust language has advanced quite a bit in that time. | |||
Many of the problems in that tutorial are no longer problems in Rust. On the | |||
downside, some of the examples in TMLL no longer work with the current version | |||
of Rust. | |||
## Extension | |||
This assignment has two few optional extensions. Note: the amount of work needed | |||
to do these extensions is almost certainly not worth it for the extra point | |||
values. You should only do these extensions if you're actually interested. | |||
### Range iterators (5 extra points) | |||
The standard `BTreeMap`s support range iterators for iterating over a range of | |||
keys. Take a look at the standard library, and implement the following: | |||
1. Borrowing range iterator (called with `.range(a..b)`) | |||
2. Mutable range iterator (called with `.range_mut(a..b)`) | |||
Some tips: | |||
* You'll want to read up on ranges and bounds | |||
https://doc.rust-lang.org/std/ops/trait.RangeBounds.html | |||
https://doc.rust-lang.org/std/ops/enum.Bound.html | |||
* You will need to adapt the iterator structs. | |||
* The standard range iterators implement a ton of other traits. Just implement | |||
the plain `Iterator` trait. | |||
* Your range iterators should take ownership of the range. | |||
* Do not iterate over items unnecessarily. For instance, do not just use a | |||
regular iterator, skip until you get into the range, and then continue from | |||
there. | |||
### Avoiding recursion (5 extra points) | |||
Recursion is not encouraged in Rust. For medium-sized data structures, recursive | |||
operations will quickly blow up the stack. Implement all operations using loops | |||
instead of recursion. | |||
**IMPORTANT NOTE**: Our reference solution hit problems with the borrow checker | |||
when implementing `remove` without recursion (we filed an issue to the Rust | |||
github). If this happens to you, give a non-recursive implementation of remove | |||
and comment it out. | |||
## Disclaimer | |||
TMLL contains a very good discussion of writing your own linked list in Rust, | |||
and the same conclusions carry over to writing BSTs. Essentially: there is no | |||
good reason to do this in practice. You're going to be way better off just using | |||
Rust's excellent standard libraries and collections, and if you're really doing | |||
something weird you will almost always use unsafe Rust to write pointer-based | |||
datastructures. The purpose of this assignment is to give you hands-on | |||
experience in one shot with many core aspects Rust: ownership, borrowing, | |||
references, pointers, iterators, traits, lifetimes, etc. If you ever need a map | |||
collection in the future, you almost certainly don't want to make your own. | |||
(This goes in essentially every language, not just Rust.) |
@@ -0,0 +1,568 @@ | |||
/* | |||
* CS 538, Spring 2019: HW5 | |||
* | |||
* Binary Search Tree | |||
* See `bst.md` for the overview. | |||
*/ | |||
#![allow(dead_code)] | |||
#![forbid(unsafe_code)] | |||
use std::cmp::Ordering; | |||
use std::fmt::Debug; | |||
use std::iter::{FromIterator, IntoIterator}; | |||
use std::ops::{Bound, Index, IndexMut}; | |||
/// Type of BST nodes. | |||
#[derive(Debug)] | |||
struct Node<K, V> { | |||
key: K, | |||
val: V, | |||
size: usize, | |||
lt: TreeMap<K, V>, | |||
rt: TreeMap<K, V>, | |||
} | |||
/// Type of BSTs. | |||
/// | |||
/// See TMLL 2.1 for details about the memory layout if you are curious. | |||
#[derive(Debug, Default)] | |||
pub struct TreeMap<K, V> { | |||
inner: Option<Box<Node<K, V>>> | |||
} | |||
/// Part 1: Basic operations and traits (50) | |||
/// | |||
/// Your first task is to implement the following operations on BSTs. | |||
/// | |||
/// The API here is modeled after Rust's standard BTreeMap collections type. The implementation | |||
/// details are up to you, but you should not change the types of public functions. | |||
impl<K, V> TreeMap<K, V> | |||
where | |||
K: Ord + Debug, | |||
V: Debug, | |||
{ | |||
/// Make a new TreeMap. | |||
pub fn new() -> Self { | |||
unimplemented!() | |||
} | |||
/// Clear a TreeMap. | |||
pub fn clear(&mut self) { | |||
unimplemented!() | |||
} | |||
/// Check if a TreeMap is empty. | |||
pub fn is_empty(&self) -> bool { | |||
unimplemented!() | |||
} | |||
/// Compute the size of a TreeMap. | |||
pub fn len(&self) -> usize { | |||
unimplemented!() | |||
} | |||
/// Check if a TreeMap has a certain key. | |||
pub fn has_key(&self, key: &K) -> bool { | |||
unimplemented!() | |||
} | |||
/// Get a reference to the value associated with a key, if present. | |||
/// | |||
/// If the key is not in the map, return None. | |||
pub fn get(&self, key: &K) -> Option<&V> { | |||
unimplemented!() | |||
} | |||
/// Get a mutable reference to the value associated with a key, if present. | |||
/// | |||
/// If the key is not in the map, return None. | |||
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { | |||
unimplemented!() | |||
} | |||
/// Insert a (key, value) pair into a TreeMap. | |||
/// | |||
/// If the key is already present in the map, return the previous value and replace the old | |||
/// value with the new value. Otherwise, insert the new (key, value) pair and return None. | |||
pub fn insert(&mut self, key: K, val: V) -> Option<V> { | |||
unimplemented!() | |||
} | |||
/// Insert a nonempty TreeMap into a TreeMap. | |||
/// | |||
/// While real BSTs do careful rotations/rebalancing to keep the depth of the merged tree as | |||
/// small as possible, we will just do a naive insert by going down to an appropriate leaf of | |||
/// `self` and sticking `other` there. | |||
/// | |||
/// Since this function is not publicly visible (there is no `pub` before `fn`), it is not used | |||
/// directly by clients. You may assume that `self` and `other` are non-overlapping: either all | |||
/// nodes in self are strictly smaller than all nodes in other, or all nodes in self are | |||
/// strictly larger than all nodes in other. Your implementation can panic (or just continue | |||
/// silently) if this requirement is not met. However if the trees are not overlapping, you | |||
/// must maintain the BST invariant. | |||
fn insert_tree(&mut self, mut other: Self) { | |||
unimplemented!() | |||
} | |||
/// Remove a key from a TreeMap. | |||
/// | |||
/// If the map contains the key, remove the key and return the associated value. | |||
/// If the map does not contain the key, return None and leave the map unchanged. | |||
/// | |||
/// Hint: take the two child trees of the node you're removing, insert one into the other. | |||
pub fn remove(&mut self, key: K) -> Option<V> { | |||
unimplemented!() | |||
} | |||
} | |||
/// We will implement a few traits for TreeMap. Starting off: implement the FromIterator trait. | |||
/// | |||
/// Hint: feed stuff from the iterator into the TreeMap. | |||
impl<K, V> FromIterator<(K, V)> for TreeMap<K, V> | |||
where | |||
K: Ord + Debug, | |||
V: Debug, | |||
{ | |||
fn from_iter<T>(iter: T) -> TreeMap<K, V> | |||
where | |||
T: IntoIterator<Item = (K, V)>, | |||
{ | |||
unimplemented!() | |||
} | |||
} | |||
/// Now, we will implement two kinds of indexing traits: Index and IndexMut. | |||
/// | |||
/// Rust's built-in syntactic sugar hooks into these traits, for instance we can write stuff like: | |||
/// | |||
/// let val = map[idx]; | |||
/// | |||
/// which is short for | |||
/// | |||
/// let val = *map.index(idx); | |||
/// | |||
/// if we have the Index trait. If we implement the IndexMut trait, we can write stuff like: | |||
/// | |||
/// map[idx] = new_val; | |||
/// | |||
/// which is short for | |||
/// | |||
/// *map.index(idx) = new_val; | |||
/// | |||
/// Index returns a reference to the value for a given key, and IndexMut returns a mutable | |||
/// reference to the value for a given key. If the key is not in the map, panic. You will probably | |||
/// want to take a look at the documentation for Index and IndexMut. | |||
/// | |||
/// Note: the Rust BTreeMap actually has a more general type for these operations, something like: | |||
/// | |||
/// fn index<Q>(&self, key: &Q) -> &V | |||
/// where | |||
/// K: Borrow<Q>, | |||
/// Q: Ord + ?Sized, | |||
/// { ... } | |||
/// | |||
/// The idea here is that K is the key type stored in the map, while Q is the key type used for | |||
/// lookups. In some cases, this allows us to avoid constructing a new object just to do a lookup; | |||
/// think K = String and Q = &str. The trait bound K: Borrow<Q> says that if we have a K, we can | |||
/// get a reference to Q. The Then, we can compare Q's since Q: Ord. (The question-mark bound Q: | |||
/// ?Sized means that the size of Q does *not* need to be known at compile time; by default, all | |||
/// type parameters have statically known sizes.) See the documentation for Borrow for an example | |||
/// of how this works: | |||
/// | |||
/// https://doc.rust-lang.org/std/borrow/trait.Borrow.html | |||
impl<'a, K, V> Index<&'a K> for TreeMap<K, V> | |||
where | |||
K: Ord + Debug, | |||
V: Debug, | |||
{ | |||
type Output = V; | |||
fn index(&self, key: &K) -> &V { | |||
unimplemented!() | |||
} | |||
} | |||
impl<'a, K, V> IndexMut<&'a K> for TreeMap<K, V> | |||
where | |||
K: Ord + Debug, | |||
V: Debug, | |||
{ | |||
fn index_mut(&mut self, key: &'a K) -> &mut V { | |||
unimplemented!() | |||
} | |||
} | |||
/// Part 2: Iterators (35) | |||
/// | |||
/// Now, you will soup up your BST by implementing various kinds of iterators to perform in-order | |||
/// traversals of the map, i.e., from smallest to largest key. | |||
/// | |||
/// The first iterator you will design is a so-called *consuming iterator*, which takes ownership | |||
/// of all elements in the map. You will use the following types to track the current state of the | |||
/// iterator: | |||
enum Next<I, T> { | |||
Item(I), | |||
Tree(T), | |||
} | |||
pub struct IntoIter<K, V> { | |||
next_nodes: Vec<Next<(K, V), TreeMap<K, V>>>, | |||
current_val: Option<(K, V)>, | |||
} | |||
/// This state has two main parts. There is the current key value pair (or nothing, if the iterator | |||
/// is already finished), and stack of `Next` recording the next elements of the iterator. For | |||
/// instance, if the iterator is currently at a node that is a left-child of its parent, the top of | |||
/// the stack would be the data at the parent, the 2nd-from-top of the stack would store the whole | |||
/// subtree from the right sibling, and so on. | |||
/// | |||
/// Define a few functions to set up the consuming iterator. | |||
impl<K, V> IntoIter<K, V> { | |||
/// Make a new consuming iterator from a TreeMap | |||
/// | |||
/// Note that this function takes ownership (i.e., tree is passed by value). This is fine, | |||
/// because we are building a *consuming* iterator. | |||
fn new(tree: TreeMap<K, V>) -> Self { | |||
unimplemented!() | |||
} | |||
/// This is the main workhorse function for setting up the iterator. In words, this function | |||
/// should descends to the left-furthermost non-leaf child. Along the way, it sets up the | |||
/// iterator state: the current value, and the next-nodes-to-visit stack. Make sure you add | |||
/// nodes in the right order: we are aiming for an in-order traversal, so the iterator should | |||
/// visit left-child, parent, right-child as we pop things off the stack. | |||
fn descend_left(&mut self, tree: TreeMap<K, V>) { | |||
unimplemented!() | |||
} | |||
} | |||
/// Implement IntoIterator for TreeMap. | |||
impl<K, V> IntoIterator for TreeMap<K, V> { | |||
type Item = (K, V); | |||
type IntoIter = IntoIter<K, V>; | |||
fn into_iter(self) -> IntoIter<K, V> { | |||
unimplemented!() | |||
} | |||
} | |||
/// Now, we are ready to implement the Iterator trait for IntoIter. | |||
/// | |||
/// The main function `next` should get the current value, update the iterator to prepare the next | |||
/// value, and then update the nodes to visit. | |||
/// | |||
/// Hint: you'll want to use `descend_left`. | |||
impl<K, V> Iterator for IntoIter<K, V> { | |||
type Item = (K, V); | |||
fn next(&mut self) -> Option<Self::Item> { | |||
unimplemented!() | |||
} | |||
} | |||
/// Now, you'll define a few variants of iterators that do not consume the data. These are also | |||
/// called *borrowing* iterators, since they borrow data instead of consuming it. The overall | |||
/// strategy will be quite similar to what you already did above, but with a few tweaks. The main | |||
/// idea behind a borrowing iterator is that we should produce references to data, rather than data. | |||
/// | |||
/// For this part, you'll be designing the type structures and implementing the necessary functions. | |||
/// You should design two kinds of iterators: | |||
/// | |||
/// 1. Borrowing iterator. This should implement the Iterator trait with item type (&K, &V). | |||
/// 2. Mutable iterator. This should implement the Iterator trait with item type (&K, &mut V). | |||
/// | |||
/// Note that we need to be a bit careful with mutable iterators: clients should be allowed to | |||
/// modify the value, but they should not be allowed to modify the keys---if they did, they might | |||
/// screw up the BST property. Your mutable iterator should hand out a pair of references: an | |||
/// *immutable* reference to the key, and a *mutable* reference to the value. | |||
/// | |||
/// To construct these two iterators, implement the following two methods for TreeMap: | |||
/// | |||
/// 1. pub fn iter(&self) -> Iter<K, V> | |||
/// 2. pub fn iter_mut(&mut self) -> IterMut<K, V> | |||
/// | |||
/// Iter and IterMut should implement the Iterator trait. | |||
/// | |||
/// You should also implement versions of all the associated functions you did for IntoIter, but do | |||
/// not implement IntoIterator for TreeMap again. | |||
/// | |||
/// Hint: don't implement these iterators from scratch. You'll need to sprinkle in some extra | |||
/// annotations and tweaks in a few key places, but your code for the borrowing and mutable | |||
/// iterators should be a nearly exact copy-paste of your code for consuming iterators. | |||
/// Part 3: Custom dropping (15) | |||
/// | |||
/// The built-in Drop trait behaves poorly because it drops TreeMap recursively. If the TreeMap | |||
/// too deep, this will blow up the stack when dropping (see TMLL 2.7 for details). | |||
/// | |||
/// Implement a custom Drop trait to drop the TreeMap in a loop rather than recursing. The default | |||
/// Drop implementation for the TreeMap/Node types drops according to a *pre-order* traversal: drop | |||
/// root, then drop left, then drop right. Your implementation should maintain this order. | |||
impl<K, V> Drop for TreeMap<K, V> { | |||
fn drop(&mut self) { | |||
unimplemented!() | |||
} | |||
} | |||
#[cfg(test)] | |||
mod test { | |||
use super::TreeMap; | |||
use std::fmt::Debug; | |||
impl<K, V> TreeMap<K, V> | |||
where | |||
K: Ord + Debug, | |||
V: Debug, | |||
{ | |||
/// Test function: checks the BST invariant. | |||
fn is_bst(&self) -> bool { | |||
let mut lsize = 0; | |||
let mut rsize = 0; | |||
if let Some(boxed) = &self.inner { | |||
let lt = &boxed.lt; | |||
let rt = &boxed.rt; | |||
if let Some(lnode) = <.inner { | |||
if lnode.key >= boxed.key || !lt.is_bst() { | |||
return false; | |||
} | |||
lsize = lnode.size; | |||
} | |||
if let Some(rnode) = &rt.inner { | |||
if rnode.key <= boxed.key || !rt.is_bst() { | |||
return false; | |||
} | |||
rsize = rnode.size; | |||
} | |||
return boxed.size == lsize + rsize + 1; | |||
} | |||
true | |||
} | |||
} | |||
#[test] | |||
fn test_insert() { | |||
let mut tree = TreeMap::new(); | |||
tree.insert(1, 11); | |||
tree.insert(3, 33); | |||
tree.insert(2, 22); | |||
assert_eq!(tree.get(&1), Some(&11)); | |||
assert_eq!(tree.get(&2), Some(&22)); | |||
assert_eq!(tree.get(&3), Some(&33)); | |||
assert!(tree.is_bst()); | |||
let insert_res = tree.insert(1, 111); | |||
assert_eq!(tree.get(&1), Some(&111)); | |||
assert_eq!(insert_res, Some(11)); | |||
assert!(tree.is_bst()); | |||
} | |||
#[test] | |||
fn test_clear() { | |||
let mut tree = TreeMap::new(); | |||
tree.insert(1, 11); | |||
tree.insert(3, 33); | |||
tree.insert(2, 22); | |||
assert_eq!(tree.len(), 3); | |||
tree.clear(); | |||
assert_eq!(tree.len(), 0); | |||
} | |||
#[test] | |||
fn test_remove() { | |||
let mut tree = TreeMap::new(); | |||
tree.insert(1, 11); | |||
tree.insert(3, 33); | |||
tree.insert(2, 22); | |||
assert_eq!(tree.remove(1), Some(11)); | |||
assert_eq!(tree.len(), 2); | |||
assert!(tree.is_bst()); | |||
assert_eq!(tree.remove(1), None); | |||
assert_eq!(tree.len(), 2); | |||
assert!(tree.is_bst()); | |||
} | |||
#[test] | |||
fn test_mut() { | |||
let mut tree = TreeMap::new(); | |||
tree.insert(1, 11); | |||
tree.insert(3, 33); | |||
tree.insert(2, 22); | |||
assert_eq!(tree.get(&3), Some(&33)); | |||
*tree.get_mut(&3).unwrap() = 333; | |||
assert_eq!(tree.get(&3), Some(&333)); | |||
assert!(tree.is_bst()); | |||
} | |||
#[test] | |||
fn test_index() { | |||
let mut tree = TreeMap::new(); | |||
tree.insert(1, 11); | |||
tree.insert(3, 33); | |||
tree.insert(2, 22); | |||
assert_eq!(tree[&1], 11); | |||
tree[&2] = 22; | |||
assert_eq!(tree.get(&2), Some(&22)); | |||
assert!(tree.is_bst()); | |||
} | |||
#[should_panic] | |||
#[test] | |||
fn test_bad_index() { | |||
let mut tree = TreeMap::new(); | |||
tree.insert(1, 11); | |||
tree.insert(3, 33); | |||
tree.insert(2, 22); | |||
tree[&5] = 10; | |||
} | |||
#[test] | |||
fn test_from_iter() { | |||
let vec = vec![(1, 11), (3, 33), (2, 22)]; | |||
let tree: TreeMap<i32, i32> = vec.into_iter().collect(); | |||
assert!(tree.is_bst()); | |||
assert_eq!(tree.get(&1), Some(&11)); | |||
assert_eq!(tree.get(&2), Some(&22)); | |||
assert_eq!(tree.get(&3), Some(&33)); | |||
} | |||
#[test] | |||
fn test_iter() { | |||
let vec = vec![(1, 11), (3, 33), (2, 22)]; | |||
let tree: TreeMap<i32, i32> = vec.into_iter().collect(); | |||
let mut iter = tree.into_iter(); | |||
assert_eq!(iter.next(), Some((1, 11))); | |||
assert_eq!(iter.next(), Some((2, 22))); | |||
assert_eq!(iter.next(), Some((3, 33))); | |||
assert_eq!(iter.next(), None); | |||
} | |||
/* Uncomment when borrowing iterator ready. | |||
#[test] | |||
fn test_borrow_iter() { | |||
let vec = vec![(1, 11), (3, 33), (2, 22)]; | |||
let tree: TreeMap<i32, i32> = vec.into_iter().collect(); | |||
let mut iter = tree.iter(); | |||
assert_eq!(iter.next(), Some((&1, &11))); | |||
assert_eq!(iter.next(), Some((&2, &22))); | |||
assert_eq!(iter.next(), Some((&3, &33))); | |||
assert_eq!(iter.next(), None); | |||
} */ | |||
/* Uncomment when mutable iterator ready. | |||
#[test] | |||
fn test_mut_iter() { | |||
let vec = vec![(1, 11), (3, 33), (2, 22)]; | |||
let mut tree: TreeMap<i32, i32> = vec.into_iter().collect(); | |||
for (k, v) in tree.iter_mut() { | |||
*v = k + 100; | |||
} | |||
assert!(tree.is_bst()); | |||
assert_eq!(tree.len(), 3); | |||
assert_eq!(tree.get(&1), Some(&101)); | |||
assert_eq!(tree.get(&2), Some(&102)); | |||
assert_eq!(tree.get(&3), Some(&103)); | |||
} */ | |||
use std::cell::RefCell; | |||
#[derive(Debug)] | |||
struct MyDropper<'a> { | |||
inner: String, | |||
log: &'a RefCell<Vec<String>>, | |||
} | |||
impl<'a> Drop for MyDropper<'a> { | |||
fn drop(&mut self) { | |||
// Record the dropped String in a shared log | |||
let newstr = self.inner.clone(); | |||
self.log.borrow_mut().push(newstr); | |||
} | |||
} | |||
#[test] | |||
fn test_drop() { | |||
let drops = RefCell::new(vec![]); | |||
// Contents of tree in pre-order traversal | |||
let contents = vec![ | |||
String::from("m"), | |||
String::from("h"), | |||
String::from("a"), | |||
String::from("k"), | |||
String::from("t"), | |||
String::from("p"), | |||
String::from("z"), | |||
]; | |||
// Set up a complete binary tree (key = val) | |||
{ | |||
let mut tree = TreeMap::new(); | |||
for s in &contents { | |||
let log = &drops; | |||
let key = s.clone(); | |||
let inner = s.clone(); | |||
tree.insert(key, MyDropper { inner, log }); | |||
} | |||
} // ... and drop it | |||
// Check the order of drops | |||
assert_eq!(*drops.borrow(), contents); | |||
} | |||
/* Uncomment when drop trait ready. | |||
#[test] | |||
fn test_drop_big() { | |||
// This is slow (~1-2 min on my machine) but it shouldn't overflow! | |||
let v = (1..100000).collect::<Vec<u32>>(); | |||
// Make a really deep binary tree | |||
{ | |||
let mut tree = TreeMap::new(); | |||
for i in v { | |||
tree.insert(i, i); | |||
} | |||
} // ... and drop it | |||
println!("All done!"); | |||
} */ | |||
} |
@@ -0,0 +1,204 @@ | |||
\documentclass{article} | |||
\usepackage{amsmath} | |||
\usepackage{amssymb} | |||
\usepackage{stmaryrd} | |||
\usepackage{mathtools} | |||
\usepackage{color} | |||
\usepackage{mathpartir} | |||
\usepackage{fullpage} | |||
\usepackage{minted} | |||
\title{Written Assignment 5} | |||
\author{CS 538, Spring 2019} | |||
\begin{document} | |||
\maketitle | |||
\begin{center} | |||
\includegraphics[width=0.6\textwidth]{images/borrowck.jpg} | |||
\end{center} | |||
\hspace*{\fill} ---~@avadacatavra | |||
\bigskip | |||
These exercises will give you practice with thinking about Rust's ownership | |||
rules and the borrow checker. For each of the following code snippets, answer | |||
the following questions in 1-2 sentences each: | |||
% | |||
\begin{enumerate} | |||
\item Explain how the program breaks Rust's rules \emph{in your own | |||
words}---do not just copy the compiler error. | |||
\item \emph{Why} is the program bad? Suppose that Rust accepted this program. | |||
What could go possibly wrong? | |||
\end{enumerate} | |||
\section*{Problem 0 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: String) { /* ... */ } | |||
fn baz() { | |||
let str_1 = String::from("Hello"); | |||
let str_2 = str_1; | |||
foo(str_1); | |||
} | |||
\end{minted} | |||
\section*{Problem 1 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: &mut String) { /* ... */ } | |||
fn baz() { | |||
let my_str = String::from("Hello"); | |||
foo(&my_str); | |||
} | |||
\end{minted} | |||
\section*{Problem 2 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn baz() { | |||
let mut my_str = String::from("Hello"); | |||
let my_ref = &my_str; | |||
my_str.push_str(" World!"); | |||
println!("What's here? {}", my_ref); | |||
} | |||
\end{minted} | |||
\section*{Problem 3 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: &mut String) { /* ... */ } | |||
fn baz() { | |||
let mut my_str = String::from("Hello"); | |||
let my_ref = &my_str; | |||
let my_other_ref = &mut my_str; | |||
foo(my_other_ref); | |||
println!("What's here? {}", my_ref); | |||
} | |||
\end{minted} | |||
\section*{Problem 4 (3)} | |||
\begin{minted}[samepage]{rust} | |||
struct MyStruct { | |||
my_str: String, | |||
my_int: i32, | |||
} | |||
fn foo(s: String) { /* ... */ } | |||
fn baz() { | |||
let my_struct = MyStruct { | |||
my_str: String::from("Hello"), | |||
my_int: 42, | |||
}; | |||
foo(my_struct.my_str); | |||
foo(my_struct.my_str); | |||
} | |||
\end{minted} | |||
\section*{Problem 5 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: String) { /* ... */ } | |||
fn bar(s: &String) { | |||
let my_str: String = *s; | |||
foo(my_str); | |||
} | |||
\end{minted} | |||
\section*{Problem 6 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: String) { /* ... */ } | |||
fn bar(opt: &Option<String>) { | |||
match opt { | |||
None => println!("Nothing!"), | |||
Some(my_str) => foo(my_str) | |||
} | |||
} | |||
\end{minted} | |||
\section*{Problem 8 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: String) { /* ... */ } | |||
fn bar(opt: Option<String>) { | |||
match opt { | |||
None => println!("Nothing!"), | |||
Some(my_str) => foo(my_str) | |||
} | |||
let opt_s = opt.unwrap_or(String::new()); | |||
println!("What's here? {}", opt_s); | |||
} | |||
\end{minted} | |||
\section*{Problem 8 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo<'a>(s: String) -> &'a String { | |||
&s | |||
} | |||
\end{minted} | |||
\section*{Problem 9 (3)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo(s: &mut String) -> &mut String { | |||
s.push_str(" World!"); | |||
s | |||
} | |||
fn baz() { | |||
let mut dummy = String::new(); | |||
let mut str_ref = &mut dummy; | |||
{ | |||
let mut my_str = String::from("Hello"); | |||
str_ref = foo(&mut my_str); | |||
} | |||
println!("What's here? {}", str_ref); | |||
} | |||
\end{minted} | |||
\section*{Problem Optional (0)} | |||
\begin{minted}[samepage]{rust} | |||
fn foo<'a>(s: &'a String, t: &'a String) -> &'a String { | |||
if s > t { s } else { t } | |||
} | |||
fn baz() { | |||
let mut foo1 = String::from("Hello"); | |||
let mut str_ref: &String; | |||
{ | |||
let mut foo2 = String::from("World"); | |||
str_ref = foo(&foo1, &foo2); | |||
} | |||
println!("What's here? {}", str_ref); | |||
} | |||
\end{minted} | |||
\end{document} |