// Use rustc to compile the program and simply run with ./warmup
use List::*;
use std::fmt::Debug;
// TODO: Write a function is_odd which takes as input an unsigned 32-bit integer
// and returns a boolean. The return value is "true" if the input integer
// is odd
fn is_odd(n: u32) -> bool {
unimplemented!();
}
// TODO: Write a function that takes as input a tuple of any two
// generic types \tau_1 and \tau_2 and returns a tuple with the
// members reversed. Your single function should be able to handle
// cases like the following:the
// (1,2) -> (2,1)
// (true, false) -> (false, true)
// (1.0, (true,1)) -> ((true,1), 1.0)
fn reverse(pair: (A, B)) -> (B, A) {
unimplemented!();
}
/*******************************************************************************
Playing with Vectors and Vector Slices:
Rust has the cool concept of ownership. All variables in the language are an
alias to some region in memory. The type of the variable (be it reference,
mutable, etc.) contains information as to the relationship ownership relations
between it and the memory it references.
The functions above all take their input by value. In the case of the simple
function `is_odd', the integer is copied before the value is passed to the
function. Any use of the variable within the function is then using this copied
value.
For more complex structures (heap-allocated) passing a variable by value has
the interesting effect of the caller relinquishing ownership of the variable to
the callee. Suppose B is some complicated structure.
fn main() {
let x = (1,B);
let rev = reverse(x);
println!("{:?}", x) // This code will fail!
}
The reason for this is that the ownership is transferred to reverse, which
relinquishes at the end of its execution. Then rust is allowed to free the memory,
and therefore x is no longer valid. Thankfully, the compiler will check for
these types of errors FOR you, thanks to the type system, so you'll be able to
catch them immediately.
Some might consider the above a bit problematic. A Rust-ian solutions is to instead
define the reverse function so that it "borrows" a reference to the memory in
pointed to by x. We can do this by declaring the input type as a reference:
fn reverse(pair: &(A, B)) -> (B, A){
...
}
In this way, it can have read access to the data but we can guarantee that it
won't change the data. By borrowing, we tell the rust compiler that the
lifetime of x should at least outlive the lifetime of our borrowed alias.
Additionally, since the contents of x are not moved, the following code is
perfectly valid:
fn main() {
let x = (1,B);
let borrowed_x = &x;
let rev = reverse(borrowed_x);
println!("{:?}", x) // This code will work!!
}
Note that we explicitly borrow above. This is not strictly necessary.
Now that you have some knowledge about Rust ownership, complete the following
three functions. Note that all of these functions take their arguments by
reference, rather than by value.
Please don't use the standard library methods of the vector class. Basic functions
such as contains() and push() are fine.
*******************************************************************************/
/// Computes the sum of all elements in the input i32 slice named `slice`
pub fn sum(slice: &[i32]) -> i32 {
// TODO
unimplemented!();
}
/// Deduplicates items in the input vector `vs`.
// Produces a vector containing the first instance of each distinct element of
// `vs`, preserving the original order.
// Hint: A HashMap might come in handy, plus you'll learn about the keyword 'mut'.
pub fn dedup(vs: &Vec) -> Vec {
// TODO
unimplemented!();
}
/// Filters a vector `vs` using a predicate `pred` (a function from `i32` to
/// `bool`). Returns a new vector containing only elements that satisfy `pred`.
pub fn filter(vs: &Vec, pred: &Fn(i32) -> bool) -> Vec {
// TODO
unimplemented!();
}
// We're using the enum keyword to create define the type of a list.
// Cons: Tuple struct that wraps an element and a pointer to the next node.
// Nil: A node that signifies the end of the linked list
enum List {
Cons(T, Box>),
Nil,
}
// Now we use the impl keyword to implement an instance of a List.
impl List {
// TODO: Creates a new list.
fn new() -> List {
unimplemented!();
}
// TODO: Consume a list, and return the same list with a new element at its front.
// Note that here pass in self as a value (so we consume it) and then return
// a new list.
fn prepend(self: List, elem: T) -> List {
unimplemented!();
}
fn append(self: List, elem: T) -> List {
unimplemented!();
}
// TODO: Return the length of the list.
// Note that we "borrow" a reference to the list (self) rather than consuming
// it. This means that len cannot modify the contents of self.
fn len(self: &List) -> u32 {
unimplemented!();
}
// Return representation of the list as a (heap allocated) string
fn stringify(self: &List) -> String {
match *self {
Cons(ref head, ref tail) => {
// `format!` is similar to `print!`, but returns a heap
// allocated string instead of printing to the console
format!("{:?}::{}", head, tail.stringify())
},
Nil => {
format!("Nil")
}
}
}
}
fn main() {
// Create an empty linked list
let mut list = List::new();
// Prepend some elements
list = list.prepend(3);
list = list.prepend(2);
list = list.prepend(1);
// Append some other list elements
list = list.append(4);
list = list.append(5);
list = list.append(6);
// Show the final state of the list
println!("Linked list has length: {}.", list.len());
println!("{}.", list.stringify());
// Create pairs
let org1 = (1,3);
let org2 = (true,false);
println!("Original is {:?} and reversed is {:?}.", org1, reverse(org1));
println!("Original is {:?} and reversed is {:?}.", org2, reverse(org2));
// Test odd function.
let odd = 3;
let even = 2;
println!("{} is odd: {}", odd, is_odd(odd));
println!("{} is odd: {}", even, is_odd(even));
// TOOD: Add more tests of your functionality.
}