/* CSC 123/252 Nightmare Test You just found out that the programming languages final is TODAY! You want to panic! but panic! in Rust is really bad!. final_exam.map(|test|println!("good luck on {}",test)); 1. (8) Select all true statments: * a. Rust does not have exceptions because exceptions are dynamically scoped b. Rust always guarantees tail-recursion optimization * c. C++ can still have memory leaks even with the exclusive use of unique_ptr and make_unique (no raw pointers) d. In a Rust expression of the form `n.f()`, dynamic dispatch is always used to determine which function f to call on object n. f. Pattern matching in F# is equivalent to multiple-dispatch in Julia g. Rust will automatically move values from stack to heap in order to extend their "lifetime", just like the language Go. * h. Calling .unwrap on an Option in Rust can result in panic (crash). i. F# is not as strongly type checked at compile time as C# j. Rust can never have a memory leak WARNING: selecting certain items may result in a score of zero regardless of what else was selected. 2. In Rust, `std::env::args()` returns an Iterator over the command-line arguments, as owned Strings, passed to main. std::evn::args().nth(1) will give you the first argument, if it exists. It returns an Option. Give a string (or slice) s, `s.parse::()` will attempt to convert the string into an unsiged integer that can be used as a size or index. It will return a Result. For example, "12".parse::() will return Ok(12) and "abc".parse:: will return Err(e) where e is a ParseIntError object (when printed, it will say "error invalid digit found in string"). The following code was written by someone who was terrible at monadic error handling (though it compiles). fn main() { let mut n = 100; // default value of n, to be changed by command-line arg let option1 = std::env::args().nth(1); if option1.is_some() { let string1 = option1.unwrap(); let result1 = string1.parse::(); if result1.is_ok() { n = result1.unwrap(); } } // ... } 2a. Rewrite the above using pattern matching. You may not call is_some/is_ok or unwrap/unwrap_or. You may only use match and if-let. fn main() { let mut n = 100; if let Some(string1) = std::env::args().nth(1) { if let Ok(x) = string1.parse::() { n = x; } } }//main1 2b. Rewrite the above using only .map and/or .and_then. No more than two calls are allowed (two .map/.and_then). For this one you may also call .unwrap_or. fn main2() { let mut n = 100; std::env::args().nth(1).map(|string1|string1.parse::() .map(|x|n=x)); } fn main3() { let n = std::env::args().nth(1) .and_then(|string1| string1.parse::().ok()) // convert result to option .unwrap_or(100); } // can't use the ? because these functions don't return Option 3. The following function takes a mutable borrow of a vector and returns a vector of mutable borrows of the values of the vector. Explain why it doesn't compile (in specific terms). fn mutborrows(v:&mut Vec) -> Vec<&mut T> { let mut i = 0; let mut mv = Vec::new(); let vlen = v.len(); while (i(v:&mut Vec) -> Vec<&mut T> { let mut mv = Vec::new(); for x in v.iter_mut() { mv.push(x); } mv } 4. Does the following compile in Rust? If not, change one line of code to make it compile. You're allowed to change at most one line. fn f() { let mut x=1; let mut bx = &x; x = 2; println!("bx is borrowing {}",bx); } //no, but the following does: fn f() { let mut x=1; let mut bx = &x; x = 2; bx = &x; // borrow fresh println!("bx is borrowing {}",bx); } 4b. How about the following: does it compile? If not, what minimal changes must be made to make it compile? Hint: it's about lifetimes. fn g(x:&i32, y:&i32) -> &i32 { if *x < *y { x } else { y } } fn g2<'t>(x:&'t i32, y:&'t i32) -> &'t i32 { if *x < *y { x } else { y } } 5. Explain what, if any memory errors will result from calling the following C++ function. */ #include using namespace std; void cpp_forever() { int *a = new int[10]; int *b = new int[10]; a = b; // memory leak delete a; delete b; // double free } /* 5b. Rewrite the function using std::unique_ptr and std::make_unique to eliminate all errors. Then EXPLAIN how the errors where eliminated: be specific to a, b in the explanation. Superfluous materials will result in lost points. */ void clean_cpp() { unique_ptr a = make_unique(10); unique_ptr b = make_unique(10); a = move(b); } /* 6. Give an example of how mixing raw pointers with unique_ptr can compromise the effectiveness of the smart pointer. Write no more than five lines of code in C++. Hint: call the unique_ptr's public constructor that initializes it with a raw pointer. */ void ra_is_not_i() { // resource acquisition is not initialization int *n = new int[10]; // RA unique_ptr a(n); // I unique_ptr b(n); // I again! uniquely not unique // this will cause a double delete } // also: memcpy(&a,&b) copies the unique pointer int main(){} /* 7. You finally wake up and realize that it's only a nightmare. final_exam was None, so final_exam.map(|test|println!("good luck on {}",test)); didn't print anything. Study hard but don't panic! */