Quizfragen zu “The Experimental Rust Book”

Systemnahe Programmierung mit Rust

https://rust-book.cs.brown.edu

Kurs: https://tha.de/~hhoegl/home/SysProgRust

H. Högl, 📧

Letzter Update: 2024-10-14

Chapter 1

Getting Started

Chapter 2

A Guessing Game

No Quiz

Chapter 3

Common Programming Concepts

Which statement best describes what it means if a variable x is immutable?

What is the keyword used after let to indicate that a variable can be mutated?

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let x = 1;
    println!("{x}");
    x += 1;
    println!("{x}");
}

This program:

( ) DOES compile. Output: __________
( ) does NOT compile


Which of the following statements is correct about the difference between using let and const to declare a variable?


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

const TWO: u32 = 1 + 1;

fn main() {
    println!("{TWO}");
}

This program:

( ) DOES compile
( ) does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let mut x: u32 = 1;
    {
        let mut x = x;
        x += 2;
    }
    println!("{x}");
}

This program:

( ) DOES compile. Output __________
( ) does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let mut x: u32 = 1;
    x = "Hello world";
    println!("{x}");
}

This program:

( ) DOES compile. Output __________
( ) does NOT compile


The largest number representable by the type i128 is:


if x : u8 = 0, what will happen when computing x - 1?


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let x: fsize = 2.0;
    println!("{x}");
}

This program:

( ) DOES compile: Output __________ ( ) does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let message = "The temperature today is:";
    let x = [message, 100];
    println!("{} {}", x[0], x[1]);
}

This program:

( ) DOES compile: Output __________ ( ) does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let t = ([1; 2], [3; 4]);
    let (a, b) = t;
    println!("{}", a[0] + t.1[0]);
}

This program:

( ) DOES compile: Output __________
( ) does NOT compile


The keyword for declaring a new function in Rust is:

Your input: ____


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn f(x) {
    println!("{x}");
}

fn main() {
    f(0);
}

This program:

( ) DOES compile: Output __________
( ) does NOT compile


In Rust, a curly-brace block like { ... } is:

1 An expression 2 A statement 3 A syntactic scope


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn f(x: i32) -> i32 {
    x + 1
}
fn main() {
    println!(
        "{}",
        f({
            let y = 1;
            y + 1
        })
    );
}

This program:

( ) DOES compile: Output __________
( ) does NOT compile


True/false: executing these two pieces of code results in the same value for x.

Snippet 1:

let x = if cond { 1 } else { 2 };

Snippet 2:

let x;
if cond {
   x = 1;
} else {
   x = 2;
}

(Note: both of these snippets compile!)


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let x = 1;
    let y = if x { 0 } else { 1 };
    println!("{y}");
}

This program:

( ) DOES compile: Output __________
( ) does NOT compile


True/false: this code will terminate (that is, it will not loop forever).

fn main() {
    let mut x = 0;
    'a: loop {
        x += 1;
        'b: loop {
            if x > 10 {
                continue 'a;
            } else {
                break 'b;
            }
        }
        break;
    }
}

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() {
    let a = [5; 10];
    let mut sum = 0;
    for x in a {
        sum += x;
    }
    println!("{sum}");
}

This program:

( ) DOES compile: Output __________
( ) does NOT compile

Chapter 4

Understanding Ownership

4.1

Which of the following is NOT a kind of undefined behavior?


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn add_suffix(mut s: String) -> String { s.push_str(” world”); s }

fn main() { let s = String::from(“hello”); let s2 = add_suffix(s); println!(“{}”, s2); }

This program: () DOES compile. Output: ****__****
() does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let s = String::from(“hello”); let s2; let b = false; if b { s2 = s; } println!(“{}”, s); }

This program: () DOES compile. Output: ******_******
() does NOT compile


Say we have a function that moves a box, like this:

fn move_a_box(b: Box) { // This space intentionally left blank }

Below are four snippets which are rejected by the Rust compiler. Imagine that Rust instead allowed these snippets to compile and run. Select each snippet that would cause undefined behavior, or select “None of the above” if none of these snippets would cause undefined behavior.

4.2 References and Borrowing

Consider the following program, showing the state of memory after the last line:

let x = Box::new(0); let y = Box::new(&x);L1

If you wanted to copy out the number 0 through y, how many dereferences would you need to use? Write your answer as a digit. For example, if the correct expression is *y, then the answer is 1.


Consider the following program, showing the state of memory after the last line:

fn get_first(vr: &Vec) -> i32 { vr[0] }

fn main() { let mut v = vec![0, 1, 2]; let n = get_first(&v); println!(“{} {}”, n, v[1]);L1 }

Which of the following best explains why v is not deallocated after calling get_first?


Consider the permissions in the following program:

let mut s = String::from(“Hello”);

let t = &mut s;

/_ here _/ t.push_str(” world”);

println!(“{}”, s);

At the point marked /_ here _/, what are the permissions on the path s? Select each permission below, or select “no permissions” if the path has no permissions.


Consider the permissions in the following program:

Which of the following best explains why strs loses and regains write permissions?


Consider this unsafe program:

let v1 = vec![1, 2, 3]; let mut v2 = v1; v2.push(4); println!(“{}”, v1[0]);L1

Which of the following best describes the point at which undefined behavior occurs in this program?

Response


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn incr(n: &mut i32) { *n += 1; } fn main() { let mut n = 1; incr(&n); println!(“{n}”); }

This program:
() DOES compile. Output: ****__****
() does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut s = String::from(“hello”); let s2 = &s; let s3 = &mut s; s3.push_str(” world”); println!(“{s2}”); }  This program:  DOES compile OR does NOT compile


Consider this Rust function that pushes a number onto the end of a vector, and then removes and returns the number from the front of the vector:

fn give_and_take(v: &Vec, n: i32) -> i32 { v.push(n); v.remove(0) }

Normally, if you try to compile this function, the compiler returns the following error:

error[E0596]: cannot borrow *v as mutable, as it is behind a & reference –> test.rs:2:5 | 1 | fn give_and_take(v: &Vec, n: i32) -> i32 { | ——— help: consider changing this to be a mutable reference: &mut Vec<i32> 2 | v.push(n); | ^^^^^^^^^ v is a & reference, so the data it refers to cannot be borrowed as mutable

Assume that the compiler did NOT reject this function. Select each (if any) of the following programs that could possibly cause undefined behavior if executed. If none of these programs could cause undefined behavior, then check “None of these programs” .

4.3 Fixing Ownership Errors

Which of the following is NOT a valid kind of fix to the issue of returning a stack reference from a function?


Let’s say a programmer tried writing the following function:

/// Returns a person’s name with “Ph.D.” added as a title fn award_phd(name: &String) -> String { let mut name = *name; name.push_str(“, Ph.D.”); name }

The Rust compiler rejects their code with the following error:

error[E0507]: cannot move out of *name which is behind a shared reference –> test.rs:3:20 | 3 | let mut name = *name; | ^^^^^ | | | move occurs because *namehas typeString, which does not implement the Copytrait | help: consider borrowing here:&*name

Given the stated purpose of the function, which of the following would be the most idiomatic fix to the program? The differences from the function above are highlighted.

Response  - fn award_phd(name: &String) -> String { let mut name = &*name; name.push_str(“, Ph.D.”); name } 

Let’s say a programmer tried writing the following function:

/// Rounds all the floats in a vector to the nearest integer, in-place fn round_in_place(v: &Vec) { for n in v { *n = n.round(); } }

The Rust compiler rejects their code with the following error:

error[E0594]: cannot assign to *n, which is behind a & reference –> test.rs:4:9 | 3 | for n in v { | - this iterator yields & references 4 | *n = n.round(); | ^^^^^^^^^^^^^^ n is a & reference, so the data it refers to cannot be written

Given the stated purpose of the function, which of the following would be the most idiomatic fix to the program? The differences from the function above are highlighted.

Response  - fn round_in_place(v: &Vec) -> Vec { let mut v2 = Vec::new(); for n in v { v2.push(n.round()); } v2 } 

Which of the following best explains why an i32 can be copied without a move, but a String cannot?


The following code snippet does not compile:

let s = String::from(“Hello world”); let s_ref = &s; let s2 = *s_ref; println!(“{s2}”);

Which of the following best describes the undefined behavior that could occur if this program were allowed to execute?

Response


The following program does not compile:

fn copy_to_prev(v: &mut Vec, i: usize) { let n = &mut v[i]; *n = v[i - 1]; }

fn main() { let mut v = vec![1, 2, 3]; copy_to_prev(&mut v, 1); }

Which of the following best describes the undefined behavior that could occur if this program were allowed to execute?

Response  


Consider this function that is a simplified variant of the function from the previous quiz:

/// Adds “Ph.D.” to a person’s name fn award_phd(name: &String) { let mut name = *name; name.push_str(“, Ph.D.”); }

The Rust compiler rejects this function with the following error:

error[E0507]: cannot move out of *name which is behind a shared reference –> test.rs:3:20 | 3 | let mut name = *name; | ^^^^^ | | | move occurs because *namehas typeString, which does not implement the Copytrait | help: consider borrowing here:&*name

Assume that the compiler did NOT reject this function. Select each (if any) of the following programs that could possibly cause undefined behavior if executed. If none of these programs could cause undefined behavior, then check “None of these programs” .

Response  - let name = String::from(“Ferris”); let name_ref = &name; award_phd(&name); println!(“{}”, name_ref); 

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut point = [0, 1]; let mut x = point[0]; let y = &mut point[1]; x += 1; *y += 1; println!(“{} {}”, point[0], point[1]); }  This program:  DOES compile OR does NOT compile

4.4 The Slice Type

Consider the variables s2 and s3 in the following program. These two variables will be located in memory within the stack frame for main. Each variable has a size in memory on the stack, not including the size of pointed data. Which statement is true about the sizes of s2 and s3?

fn main() { let s = String::from(“hello”); let s2: &String = &s; let s3: &str = &s[..]; }  


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut s = String::from(“hello”); for &item in s.as_bytes().iter() { if item == b’l’ { s.push_str(” world”); } } println!(“{s}”); }  This program:  DOES compile OR does NOT compile

4.5 Ownership Recap

Say you are writing a function with the following spec:

round_all takes as input a list of floating point numbers, and it rounds each number in-place to the nearest integer.

Which of the following is the most appropriate type signature for a function implementing this spec?


Say you are writing a function with the following spec:

find_contains takes as input a collection of strings and a target substring. It returns a list of all the strings in the collection that contain the target substring.

Which of the following is the most appropriate type signature for a function implementing this spec?


Rust normally disallows multiple mutable accesses to the same array, even when those accesses are disjoint. For example, this function does not compile:

fn main() { let mut v = vec![0, 1, 2, 3]; let (r0, r1) = (&mut v[0..2], &mut v[2..4]); r0[0] += 1; r1[0] += 1;
}

However, the Rust standard library has a function slice::split_at_mut that does permit this functionality:

fn main() { let mut v = vec![0, 1, 2, 3]; let (r0, r1) = v.split_at_mut(2); r0[0] += 1; r1[0] += 1;
}

Which of the following best describes how it’s possible to implement split_at_mut?


Consider the permissions in the following program:

❓ let s = String::new();» let s_ref = &

 Which of the following best explains why *s_ref does not have the O (own) permission?


Consider the set of Rust programs that contain no unsafe code. Select each of the following statements that is true about the kinds of programs accepted and rejected by the borrow checker:


fn extract(b: &Box) -> i32 { let b2: Box = b; b2 }

Imagine that the borrow checker did not reject this function. Determine whether there exists an input such that the function would cause undefined behavior if executed on that input.


The function transfer_string is rejected by the borrow checker:

fn get_first(strs: &mut (String, String)) -> &mut String { &mut strs.0 }

fn get_second(strs: &mut (String, String)) -> &mut String { &mut strs.1 }

fn transfer_string(strs: &mut (String, String)) { let fst = get_first(strs); let snd = get_second(strs); fst.push_str(snd); snd.clear(); }

Imagine that the borrow checker did not reject this function. Determine whether there exists an input such that the function would cause undefined behavior if executed on that input.

Response 

Chapter 5

Using Structs

5.1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

struct Point { x: i32, y: i32, }

fn main() { let mut a = Point { x: 1, y: 2 }; a.x += 1; let b = Point { y: 1, ..a }; a.x += 1; println!(“{}”, b.x); }  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

struct Point { x: i32, y: i32, } fn main() { let mut p = Point { x: 1, y: 2 }; let x = &mut p.x; let y = &mut p.y; x += 1; y += 1; println!(“{} {}”, p.x, p.y); }  This program:  DOES compileOR does NOT compile

5.2

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

#[derive(Debug)] struct Rectangle { width: u32, height: u32, }

fn main() { let rect1 = Rectangle { width: 30, height: 50, }; let a = area(rect1); println!(“{} * {} = {}”, rect1.width, rect1.height, a); }

fn area(rectangle: Rectangle) -> u32 { rectangle.width * rectangle.height }  This program:  DOES compile OR does NOT compile


Which statement best describes a difference between the Display and Debug traits?

5.3

What is the keyword for constructor functions in Rust?


struct Point(i32, i32); fn main() { let p = Point(1, 2); impl p { fn x(&self) -> i32 { self.0 } }

println!(“{}”, p.x()); }  This program:  DOES compile OR does NOT compile


Say you have a variable v of type &mut Vec, and you want to call the len method with the following signature:

impl Vec { fn len(&self) -> usize { /_ … _/ } }

If you try to compile the expression v.len(), which of the following statements best describes what happens?


Consider these two methods that increment a field of a struct. Which style would be more idiomatic for Rust?

struct Point(i32, i32); impl Point { fn incr_v1(mut self) { self.0 += 1; } fn incr_v2(&mut self) { self.0 += 1; } }  


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

struct Point(i32, i32);

impl Point { fn incr_x(&mut self) { self.0 += 1; } }

fn main() { let mut p = Point(0, 0); p.incr_x(); println!(“{}”, p.0); }  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

struct Point { x: i32, y: i32 }

impl Point { fn get_x(&mut self) -> &mut i32 { &mut self.x } }

fn main() { let mut p = Point { x: 1, y: 2 }; let x = p.get_x(); x += 1; println!(“{} {}”, x, p.y); }  This program:  DOES compile OR does NOT compile

Chapter 6

Enums and Pattern Matching

6.1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn foo(x: &i32) { println!(“{x}”); }

fn main() { let x = null; foo(x); }  This program:  DOES compile OR does NOT compile


Consider these two representations of a Result type that contains a value T if a computation succeeds, or an error E if it fails.

struct Result1<T, E> { ok: Option, err: Option, }

enum Result2<T, E> { Ok(T), Err(E) }

The enum Result2 is considered more idiomatic than the struct Result1 in Rust. Which statement below is NOT a valid reason why?


6.2

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

enum Location { Point(i32), Range(i32, i32) }

fn main() { let l: Location = Location::Range(0, 5); let n = match l { Location::Point() => -1, Location::Range(, n) => n, Location::Range(0, ) => 0, => -2 }; println!(“{n}”); }  This program:  DOES compile OR does NOT compile


Consider this method implemented for the Option type:

impl Option { fn unwrap_or(self, other: T) -> T { match self { Some(t) => t, None => other } } }

Which sentence best describes the behavior of this function?


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

#[derive(Debug)] enum Either { Left(usize), Right(String) }

fn main() { let x = Either::Right(String::from(“Hello world”)); let value = match x { Either::Left(n) => n, Either::Right(s) => s.len() }; println!(“{x:?} {value}”); }  This program:  DOES compile OR does NOT compile


Consider these two implementations of a function to decrement an unsigned number twice.

fn decr_twice_v1(n: u32) -> Option { match n { 0 => None, 1 => None, n2 => Some(n2 - 2) } }

fn decr_twice_v2(n: u32) -> Option { if n == 0 { None } else if n == 1 { None } else { Some(n - 2) } }

The functions have the same behavior for:  


6.3

Which control flow construct would be most idiomatic to use in the following function?

enum Location { Point(i32), Range(i32, i32) }

fn print_range_max(loc: &Location) {
// print the second field of Range, if loc is a Range }

Response 


Which control flow construct would be most idiomatic to use in the following function?

enum Location { Point(i32), Range(i32, i32) }

fn get_start(loc: &Location) -> i32 { // return the first field of Range or the only field of Point
}

Response  

6.4

Ownership Inventory #1 (uses “in-browser IDE”) – geht bei mir nur in Firefox!

A few important caveats about this experimental technology:

PLATFORM COMPATIBILITY: the in-browser IDE does not work on touch-screens. The in-browser IDE has only been tested to work on Google Chrome 109 and Firefox 107. It definitely does not work in Safari.

MEMORY USAGE: the in-browser IDE uses a WebAssembly build of rust-analyzer, which can take up a fair amount of memory. Each instance of the IDE appears to take around ~300 MB. (Note: we have also received some reports of >10GB memory usage.)

SCROLLING: the in-browser IDE will “eat” your cursor if your cursor intersects with the editor while scrolling. If you’re having trouble scrolling the page, try moving your cursor onto the rightmost scrollbar.

LOAD TIMES: the IDE may take up to 15 seconds to initialize for a new program. It will say “Loading…” as you interact with code in the editor.


Program:

/// Makes a string to separate lines of text, /// returning a default if the provided string is blank fn make_separator(user_str: &str) -> &str { if user_str == “” { let default = “=”.repeat(10); &default } else { user_str } }

If you tried to compile this function, which of the following best describes the compiler error you would get?


Program:

/// Makes a string to separate lines of text, /// returning a default if the provided string is blank fn make_separator(user_str: &str) -> &str { if user_str == “” { let default = “=”.repeat(10); &default } else { user_str } }

Normally if you try to compile this function, the compiler returns the following error:

error[E0515]: cannot return reference to local variable default –> test.rs:6:9 | 6 | &default | ^^^^^^^^ returns a reference to data owned by the current function Assume that the compiler did NOT reject this function. Which (if any) of the following programs would (1) pass the compiler, and (2) possibly cause undefined behavior if executed? Check each program that satisfies both criteria, OR check “None of these programs” if none are satisfying.

Response 


Program

/// Makes a string to separate lines of text, /// returning a default if the provided string is blank fn make_separator(user_str: &str) -> &str { if user_str == “” { let default = “=”.repeat(10); &default } else { user_str } }

Of the following fixes (highlighted in yellow), which fix best satisfies these three criteria:

/// Gets the string out of an option if it exists, /// returning a default otherwise fn get_or_default(arg: &Option) -> String { if arg.is_none() { return String::new(); } let s = arg.unwrap(); s.clone() }

If you tried to compile this function, which of the following best describes the compiler error you would get?

Response 


/// Gets the string out of an option if it exists, /// returning a default otherwise fn get_or_default(arg: &Option) -> String { if arg.is_none() { return String::new(); } let s = arg.unwrap(); s.clone() }

Normally if you try to compile this function, the compiler returns the following error:

error[E0507]: cannot move out of *arg which is behind a shared reference –> test.rs:7:13 | 7 | let s = arg.unwrap(); | ^^^^——– | | | | | *arg moved due to this method call | help: consider calling .as_ref() or .as_mut() to borrow the type’s contents | move occurs because *arg has type Option<String>, which does not implement the Copy trait

Assume that the compiler did NOT reject this function. Which (if any) of the following programs would (1) pass the compiler, and (2) possibly cause undefined behavior if executed? Check each program that satisfies both criteria, OR check “None of these programs” if none are satisfying.

/// Gets the string out of an option if it exists, /// returning a default otherwise fn get_or_default(arg: &Option) -> String { if arg.is_none() { return String::new(); } let s = arg.unwrap(); s.clone() }

Of the following fixes (highlighted in yellow), which fix best satisfies these three criteria:

Chapter 7

Packages, Crates and Modules

7.1

Which is the correct order, where “A > B” means “A contains B”?

Response  


Imagine you see a Rust package foobar with the following files:

foobar ├── Cargo.toml ├── build.rs └── src/ ├── main.rs ├── util.rs ├── lib.rs └── bin/ └── alt.rs

How many crates does this package contain? Write your answer as a digit, e.g. 0, 1, and so on.

Response

Answer: ****_****


7.2

Which of the following is NOT a benefit of using modules?

Response  


7.3

What is the keyword you use at the start of an absolute path to an item in the current crate?




Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

pub mod foo { fn a() { println!(“a”); } mod bar { pub fn b() { println!(“b”); } } } fn main() { foo::bar::b(); }

This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

pub mod foo { pub mod bar { pub fn b() { println!(“b”); } } pub fn a() { bar::b(); } } fn main() { foo::a(); }

This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

pub mod a { pub mod b { pub fn f() { println!(“b1”); } pub mod c { pub fn f() { println!(“c1”); } } } pub fn entry() { super:🅱️:c::f(); } }

pub mod b { pub fn f() { println!(“b2”); } pub mod c { pub fn f() { println!(“c2”); } } }

fn main() { crate:🅰️:entry(); }

Response  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

pub mod point { #[derive(Debug)] pub struct Point(i32, i32); impl Point { pub fn origin() -> Self { Point(0, 0) } } }

fn main() { let mut p = point::Point::origin(); p.0 += 1; println!(“{p:?}”); }

Response  This program:  DOES compile OR does NOT compile

7.4

Which of the following statements best describes the function of the use keyword?


Consider this module and use statement:

pub mod parent { pub fn a() {} fn b() {} pub mod child { pub fn c() {} } }

fn main() { use parent::{*, child as alias}; // … }

Inside main, what is the total number of paths that can refer to a, b, or c (not including those that use self, super, or crate)? Write your answer as a digit such as 0 or 1. For example, if the only two valid paths were a and parent::b, then the answer would be 2.

Answer: **_**

7.5

Imagine a Rust package with the following directory structure:

foobar ├── Cargo.toml └── src/ ├── lib.rs ├── engine.rs └── engine/ └── analysis.rs

The contents of each file are:

// engine/analysis.rs pub fn run() {}

// engine.rs mod analysis; pub use analysis::*;

// lib.rs pub mod engine;

Say that another Rust developer is using the foobar library crate in a separate package, and they want to call the run function. What is the path they would write?

Chapter 8

Common Collections

8.1

Which call to this find_until function will cause a runtime panic?

fn find_until(v: &Vec, n: i32, til: usize) -> Option { for i in 0 .. til { if v[i] == n { return Some(i); } } return None; }

Response 


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut v = Vec::new(); let s = String::from(“Hello”); v.push(s); v[0].push_str(“world”); println!(“original: {}”, s); println!(“new: {}”, v[0]); }

Response  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let v = vec![String::from(“Hello”)]; let mut s = v[0]; s.push_str(“world”); println!(“{s}”); }

Response  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut v = vec![1, 2, 3]; for i in &mut v { v.push(*i); } println!(“{} {} {}”, v[3], v[4], v[5]); }

Response  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut v: Vec = vec![1, 2, 3]; let mut v2: Vec<&mut i32> = Vec::new(); for i in &mut v { v2.push(i); } v2[0] = 5; let a = v2[0]; let b = v[0]; println!(“{a} {b}”); }

Response  This program:  DOES compile OR does NOT compile

8.2

What is the difference between using a + b and a.push_str(b) to concatenate two strings?

Response 


What is the maximum number of times a heap allocation could occur in this program? Write your answer in digits, e.g. 0 or 1.

let s1 = String::from(“tic”); let s2 = String::from(“tac”); let s3 = String::from(“toe”); let s = s1 + “-” + &s2 + “-” + &s3;  Answer: ____


Which statement is the best explanation for why Rust does not allow string indexing?

Response  


Which statement best describes the difference between the types of a string slice &str and a byte slice &[u8]?

Response  


8.3

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::collections::HashMap; fn main() { let mut h = HashMap::new(); h.insert(“k1”, 0); let v1 = &h[“k1”]; h.insert(“k2”, 1); let v2 = &h[“k2”]; println!(“{} {}”, v1, v2); }

Response  This program:  DOES compile OR does NOT compile


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::collections::HashMap; fn main() { let mut h: HashMap<char, Vec> = HashMap::new(); for (i, c) in “hello!”.chars().enumerate() { h.entry(c).or_insert(Vec::new()).push(i); } let mut sum = 0; for i in h.get(&‘l’).unwrap() { sum += *i; } println!(“{}”, sum); }

Response  This program:  DOES compile OR does NOT compile

8.4 Ownership Inventory #2 (inspired by common stackoverflow questions)

/// Removes all the zeros in-place from a vector of integers. fn remove_zeros(v: &mut Vec) { for (i, t) in v.iter().enumerate().rev() { if *t == 0 { v.remove(i); v.shrink_to_fit(); } } }

If you tried to compile this function, which of the following best describes the compiler error you would get?

Response 


/// Removes all the zeros in-place from a vector of integers. fn remove_zeros(v: &mut Vec) { for (i, t) in v.iter().enumerate().rev() { if *t == 0 { v.remove(i); v.shrink_to_fit(); } } }

Normally if you try to compile this function, the compiler returns the following error:

error[E0502]: cannot borrow *v as mutable because it is also borrowed as immutable –> test.rs:5:13 | 3 | for (i, t) in v.iter().enumerate().rev() { | ————————– | | | immutable borrow occurs here | immutable borrow later used here 4 | if *t == 0 { 5 | v.remove(i); | ^^^^^^^^^^^ mutable borrow occurs here

Assume that the compiler did NOT reject this function. Which (if any) of the following programs would (1) pass the compiler, and (2) possibly cause undefined behavior if executed? Check each program that satisfies both criteria, OR check “None of these programs” if none are satisfying.

/// Removes all the zeros in-place from a vector of integers. fn remove_zeros(v: &mut Vec) { for (i, t) in v.iter().enumerate().rev() { if *t == 0 { v.remove(i); v.shrink_to_fit(); } } }

Of the following fixes (highlighted in yellow), which fix best satisfies these three criteria:

  1. The fixed function passes the Rust compiler,
  2. The fixed function preserves the intention of the original code, and
  3. The fixed function does not introduce unnecessary inefficiencies

// Reverses the elements of a vector in-place fn reverse(v: &mut Vec) { let n = v.len(); for i in 0 .. n / 2 { std::mem::swap(&mut v[i], &mut v[n - i - 1]); } }

If you tried to compile this program, which of the following best describes the compiler error you would get?

Response  


/// Reverses the elements of a vector in-place fn reverse(v: &mut Vec) { let n = v.len(); for i in 0 .. n / 2 { std::mem::swap(&mut v[i], &mut v[n - i - 1]); } }

Normally if you try to compile this function, the compiler returns the following error:

error[E0499]: cannot borrow *v as mutable more than once at a time –> test.rs:5:40 | 5 | std::mem::swap(&mut v[i], &mut v[n - i - 1]); | ————– - ^ second mutable borrow occurs here | | | | | first mutable borrow occurs here | first borrow later used by call

Assume that the compiler did NOT reject this function. Which (if any) of the following programs would (1) pass the compiler, and (2) possibly cause undefined behavior if executed? Check each program that satisfies both criteria, OR check “None of these programs” if none are satisfying.

// Reverses the elements of a vector in-place fn reverse(v: &mut Vec) { let n = v.len(); for i in 0 .. n / 2 { std::mem::swap(&mut v[i], &mut v[n - i - 1]); } }

Of the following fixes (highlighted in yellow), which fix best satisfies these three criteria:

  1. The fixed function passes the Rust compiler,
  2. The fixed function preserves the intention of the original code, and
  3. The fixed function does not introduce unnecessary inefficiencies

Chapter 9

Error Handling

9.1

What is the name of the environment variable you should set to 1 to see the backtrace of a panic?

Answer: ____

Which of the following is NOT a good reason to use a panic?

9.2

Which of these statements best describes why File::open returns a Result and not an Option?


Given an arbitrary expression e of type Result<T, E>, which code snippet best represents how e? is translated?Given an arbitrary expression e of type Result<T, E>, which code snippet best represents how e? is translated?


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

// assume hello.txt has the contents “will” fn read_username_from_file() -> Option { let mut username_file = File::open(“hello.txt”)?; let mut username = String::new(); username_file.read_to_string(&mut username)?; Some(username) }

fn main() { println!(“{}”, read_username_from_file().unwrap()); }

 This program:  DOES compile OR does NOT compile

9.3

A Rust programmer is designing a library for writing command-line interfaces. As a part of this library, they are implementing a function to parse command-line flags provided by a user. Which implementation would be most appropriate for this domain?

fn parse_flag_v1(flag: &str) -> Result<String, String> { match flag.strip_prefix(“–”) { Some(no_dash) => Ok(no_dash.to_string()), None => Err(format!(“Invalid flag {flag}”)) } }

fn parse_flag_v2(flag: &str) -> String { match flag.strip_prefix(“–”) { Some(no_dash) => no_dash.to_string(), None => panic!(“Invalid flag {flag}”) } }

Response 

Chapter 10

Generic Types, Traits and Lifetimes

10.1

Imagine using a third-party function whose implementation you don’t know, but whose type signature is this:

fn mystery(x: T) -> T { // ???? }

Then you call mystery like this:

let y = mystery(3);

Assuming mystery uses no unsafe code, then the value of y must be:

Answer: ****__****


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn print_slice(v: &[T]) { for x in v { println!(“{x}”); } }

fn main() { print_slice(&[1, 2, 3]); }

Response  This program: DOES compile OR does NOT compile

Output: ?

Hinweis:

fn print_slice<T: std::fmt::Display>(v: &[T]) {

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

struct Point { x: T, y: T } impl Point { fn f(&self) -> &i32 { &self.y } }

impl Point { fn f(&self) -> &T { &self.x } }

fn main() { let p: Point = Point { x: 1, y: 2 }; println!(“{}”, p.f()); }  This program:  DOES compile OR does NOT compile

10.2

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

trait MakeNoise { fn make_noise(&self) { println!(“(silence)”); } }

struct Dog {}

struct Cat {}

impl MakeNoise for Dog { fn make_noise(&self) { println!(“bark”); } }

impl MakeNoise for Cat {}

fn main() { let dog = Dog {}; let cat = Cat {}; dog.make_noise(); cat.make_noise(); }

Does compile | Does not compile


The following are statements about what kinds of trait implementations are permitted by Rust. Select each statement which is true.

Loesung: The “orphan rule” requires that you cannot implement an external trait for an external type, to ensure code doesn’t break if two crates provide conflicting implementations.

10.3

Which kind of programming error is a lifetime supposed to prevent?


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn shortest<’a, ’b>(x: &’a str, y: &’b str) -> &’a str { if x.len() < y.len() { x } else { y } }

fn main() { println!(“{}”, shortest(“hello”, “rust”)); }

 This program:  DOES compile OR does NOT compile

Context: If the type signature says that the function must return a reference with lifetime ’a, then it would be invalid to return a reference with a different lifetime ’b, i.e. y here.


If a reference has a lifetime ’static, then this means:

Response  


Consider the following un-annotated function signature.

struct Foo<’a> { bar: &’a i32 }

fn baz(f: Foo) -> &i32 { /_ … _/ }

Will Rust accept this function signature? If so, what lifetimes will it infer?


Consider the following un-annotated function signature.

struct Foo<’a> { bar: &’a i32 }

// Foo changed to &Foo fn baz(f: &Foo) -> &i32 { /_ … _/ }

Will Rust accept this function signature? If so, what lifetimes will it infer?

Response  

Mit der angegebenen Signatur wird es nicht kompiliert. Wenn man es wie folgt aendert kompiliert es!

struct Foo<’a> {
bar: &’a i32,
}
// Foo changed to &Foo
// fn baz(f: &Foo) -> &i32 {
fn baz<’a>(f: &’a Foo<’a>) -> &’a i32 {
f.bar
}

fn main() {
let a = 42;
let F = Foo { bar: &a };
let r = baz(&F);
println!(“{r}”);
}

Abschliessend weitere 6 Fragen:

Ownership Inventory #3

Program 1:

1 /// Returns the n-th largest element in a slice 2 fn find_nth<T: Ord + Clone>(elems: &[T], n: usize) -> T { 3 elems.sort(); 4 let t = &elems[n]; 5 return t.clone(); 6 }

If you tried to compile this program, which of the following best describes the compiler error you would get?


Program 1:

/// Returns the n-th largest element in a slice fn find_nth<T: Ord + Clone>(elems: &[T], n: usize) -> T { elems.sort(); let t = &elems[n]; return t.clone(); }

Normally if you try to compile this function, the compiler returns the following error:

error[E0596]: cannot borrow *elems as mutable, as it is behind a & reference –> test.rs:3:5 | 3 | elems.sort(); | ^^^^^^^^^^^^ elems is a & reference, so the data it refers to cannot be borrowed a

Assume that the compiler did NOT reject this function. Which (if any) of the following programs would (1) pass the compiler, and (2) possibly cause undefined behavior if executed? Check each program that satisfies both criteria, OR check “None of these programs” if none are satisfying.

Response  - None of these programs

/// Returns the n-th largest element in a slice fn find_nth<T: Ord + Clone>(elems: &[T], n: usize) -> T { elems.sort(); let t = &elems[n]; return t.clone(); }

Of the following fixes (highlighted in yellow), which fix best satisfies these three criteria:

(die gelbe Einfaerbung fehlt leider)

Response


struct TestResult { /// Student’s scores on a test scores: Vec,

/// A possible value to curve all scores
curve: Option<usize>

}

impl TestResult {
pub fn get_curve(&self) -> &Option { &self.curve }

/// If there is a curve, then increments all
/// scores by the curve
pub fn apply_curve(&mut self) {
    if let Some(curve) = self.get_curve() {
        for score in self.scores.iter_mut() {
            *score += *curve;
        }
    }
}

}

If you tried to compile this program, which of the following best describes the compiler error you would get?

Response  


struct TestResult { /// Student’s scores on a test scores: Vec,

/// A possible value to curve all scores
curve: Option<usize>

}

impl TestResult {
pub fn get_curve(&self) -> &Option { &self.curve }

/// If there is a curve, then increments all
/// scores by the curve
pub fn apply_curve(&mut self) {
    if let Some(curve) = self.get_curve() {
        for score in self.scores.iter_mut() {
            *score += *curve;
        }
    }
}

}

Normally if you try to compile this function, the compiler returns the following error:

error[E0502]: cannot borrow self.scores as mutable because it is also borrowed as immutable –> test.rs:17:26 | 16 | if let Some(curve) = self.get_curve() { | —————- immutable borrow occurs here 17 | for score in self.scores.iter_mut() { | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here 18 | score += curve; | —— immutable borrow later used here

Assume that the compiler did NOT reject this function. Which (if any) of the following programs would (1) pass the compiler, and (2) possibly cause undefined behavior if executed? Check each program that satisfies both criteria, OR check “None of these programs” if none are satisfying.

Response  - let mut result = TestResult { scores: vec![20, 50, 30], curve: Some(10) }; let x = &result.scores[0]; result.apply_curve(); println!(“{}”, x); 

struct TestResult { /// Student’s scores on a test scores: Vec,

/// A possible value to curve all scores
curve: Option<usize>

}

impl TestResult {
pub fn get_curve(&self) -> &Option { &self.curve }

/// If there is a curve, then increments all
/// scores by the curve
pub fn apply_curve(&mut self) {
    if let Some(curve) = self.get_curve() {
        for score in self.scores.iter_mut() {
            *score += *curve;
        }
    }
}

}

Of the following fixes (highlighted in yellow), which fix best satisfies these three criteria:

  1. The fixed function passes the Rust compiler,
  2. The fixed function preserves the intention of the original code, and
  3. The fixed function does not introduce unnecessary inefficiencies

Reponse

Chapter 11

Tests

11.1

What is the annotation you add to a function to indicate that it’s a unit test?

Answer: ****__****

Let’s say you have a function with the type signature:

fn f(x: usize) -> Result<usize, String>;

And you want to test that f(0) should return Err(_). Which of the following is NOT a valid way to test that?

Response 

When running cargo test with no additional configuration, which of the following actions may not work correctly if done by multiple tests?

Response  


Consider a program with the following unit test:

#[test] fn test_the_logger() { /_ … _/ }

#[test] fn test_the_database() { /_ … _/ }

#[test] fn test_logger_and_database() { /_ … _/ }

What is the shortest string you can pass to cargo test such that only test_the_logger and test_the_database are executed?

Response: ****_****


11.3

Which of the following is NOT a good reason to wrap unit tests in #[cfg(test)] mod tests { … }?

Response 

Chapter 12

Building a CLI tool

Kein Quiz

Chapter 13

Iterators and Closures

13.1

Which of the following best describes the rationale for why Rust will infer the types of arguments/returns for closures, but not top-level functions?

Response  


Rust permits pattern matching within closure arguments, including the use of the underscore. For example, you could write the following:

let f = |_| (); // sometimes called the “toilet closure” let s = String::from(“Hello”); f(s);

Which of the following best describes the relationship between f and s in this program?

Response  

13.2

Which of the following best describes why iterators are described as “lazy”?

Response 


True/false: these two code snippets are semantically equivalent.

Snippet 1:

while let Some(x) = iter.next() { f(x); }

Snippet 2:

for x in iter { f(x); }

Response  


Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let v = vec![1, 2, 3, 4]; let a: Vec<> = v.iter().filter(|x: &&i32| x % 2 == 0).map(|x: &i32| x 2).collect(); let b: Vec<> = v.iter().map(|x: &i32| x * 2).filter(|x: &i32| x % 2 == 0).collect(); println!(“{} {}”, a[0], b[0]); }

This program:  DOES compile OR does NOT compile

13.3

Kein Quiz

13.4

Kein Quiz

Chapter 14

More about Cargo and Crates.io

Tests?

Chapter 15

Smart Pointer

15.1

Question 1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

fn main() { let mut n = 1; let b = Box::new(&mut n); **b += 1; println!(“{}”, n); }  This program:  DOES compile OR does NOT compile


Question 2

Say we have a program with a variable:

let x: [Box<(usize, usize)>; 4] = /_ … _/

For a compile target with a 64-bit architecture, what is the minimum possible size in memory (in bytes) of x on the stack? Write your answer in digits, e.g. 0, 1, so on.

Response:


15.2

Question 1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::ops::Deref;

#[derive(Clone, Copy)] struct AccessLogger(i32); impl Deref for AccessLogger { type Target = i32; fn deref(&self) -> &Self::Target { println!(“deref”); &self.0 } }

fn main() { let n = AccessLogger(-1); let x = n + 1; let n2 = n; println!(“{} {}”, x, n) }  This program:  DOES compile OR does NOT compile

15.3

Question 1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

struct Example(i32); impl Drop for Example { fn drop(&mut self) { self.0 += 1; println!(“drop {}”, self.0); } }

fn main() { let e = Example(0); drop(e); drop(e); }  This program:  DOES compile OR does NOT compile

Question 2

Consider this snippet that allocates a string:

fn main() { let mut s = String::new(); ******___****** }

Which of the following are valid operations to fill in the underscore that would cause s to be dropped?

15.4

Question 1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::rc::Rc;

fn main() { let n = Rc::new(1); let mut n2 = Rc::clone(&n); *n2 += 1; println!(“{}”, n); }

This program: DOES compile OR does NOT compile

Question 2

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::rc::Rc;

struct Example; impl Drop for Example { fn drop(&mut self) { println!(“drop”); } }

fn main() { let x = Rc::new(Example);
let y = Rc::clone(&x);
println!(“A”); drop(x); println!(“B”); drop(y);
println!(“C”); }  This program:  DOES compile OR does NOT compile

15.5

Question 1

Which of the following best describes the concept of interior mutability in Rust?

Question 2

Consider an API that tracks the number of calls to a particular method:

struct Api { count: ??? }

impl Api { fn some_method(&self) { // increment count // rest of the method… }
}

Say the count is represented as a usize. Which of the following would be the most appropriate wrapper type to use for this situation? 

-RefCell

Question 3

Consider the following incorrect implementation of a RefCell that does not check whether the interior value is borrowed:

use std::cell::UnsafeCell;

struct BadRefCell(UnsafeCell); impl BadRefCell { pub fn borrow_mut(&self) -> &mut T { unsafe { &mut *self.0.get() } } }

Now say we have a BadRefCell like this:

let v = BadRefCell(UnsafeCell::new(vec![1, 2, 3]));

Which of the following snippets would violate memory safety using this API?

15.6

Question 1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::rc::Rc;

fn main() { let r1 = Rc::new(0); let r4 = { let r2 = Rc::clone(&r1); Rc::downgrade(&r2) }; let r5 = Rc::clone(&r1); let r6 = r4.upgrade(); println!(“{} {}”, Rc::strong_count(&r1), Rc::weak_count(&r1)); }  This program:  DOES compile OR does NOT compile

Chapter 16

Fearless concurrency

16.1

Question 1

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::thread; fn main() { let mut n = 1; let t = thread::spawn(move || { n = n + 1; thread::spawn(move || { n = n + 1; }) }); n = n + 1; t.join().unwrap().join().unwrap(); println!(“{n}”); }

[ ] Does compile [ ] Does not compile

Question 2

Consider this example from the text where a vector is improperly captured by a thread:

use std::thread; fn main() { let v = vec![1, 2, 3]; let handle = thread::spawn(|| { println!(“Here’s a vector: {:?}”, v); }); handle.join().unwrap(); } The Rust compiler includes this diagnostic:

note: function requires argument type to outlive 'static –> src/main.rs:6:18 | 6 | let handle = thread::spawn(|| { | ********__********^ 7 | | println!(“Here’s a vector: {:?}”, v); 8 | | }); | |**__**^

Recall that ’static is the lifetime of references that are valid for the entire program’s duration.

Which of the following best describes the note “function requires argument type to outlive ’static”?

16.2

Question 1 Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::{sync::mpsc, thread}; enum ClientMessage { Incr, Get, Quit } enum ServerMessage { Get(usize) } fn main() { let (server_tx, client_rx) = mpsc::channel(); let (client_tx, server_rx) = mpsc::channel(); let server = thread::spawn(move || { let mut n = 0; loop { match server_rx.recv().unwrap() { ClientMessage::Quit => break, ClientMessage::Incr => n += 1, ClientMessage::Get => server_tx.send(ServerMessage::Get(n)).unwrap() } } }); for msg in [ClientMessage::Incr, ClientMessage::Get, ClientMessage::Quit] { client_tx.send(msg).unwrap(); } if let ServerMessage::Get(n) = client_rx.recv().unwrap() { println!(“{}”, n) } server.join().unwrap(); } 🐞 Response  This program:  DOES compileOR does NOT compile

Question 2

Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::{sync::mpsc, thread}; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let s = String::from(“Hello world”); tx.send(s.clone()).unwrap(); tx.send(s.len()).unwrap(); }); let s = rx.recv().unwrap(); let n = rx.recv().unwrap(); println!(“{s} {n}”); } 🐞 Response  This program:  DOES compileOR does NOT compile

16.3

Question 1 In some concurrency APIs, a mutex is separate from the data it guards. For example, imagine a hypothetical Mutex API like this:

let mut data = Vec::new(); let mx: Mutex = Mutex::new(); { let _guard = mx.lock(); data.push(0); } Which of the following best describes why Rust uses Mutex instead of just Mutex?

Question 2 Determine whether the program will pass the compiler. If it passes, write the expected output of the program if it were executed.

use std::{sync::Arc, thread}; fn main() { let s = String::from(“Hello world”); let a = Arc::new(&s); let a2 = Arc::clone(&a); let t = thread::spawn(move || a2.len()); let len = t.join().unwrap(); println!(“{} {}”, a, len); } 🐞 Response  This program:  DOES compileOR does NOT compile

16.4

Question 1

Imagine you are designing an API for a database connection like this:

struct DbConnection { /_ … / } impl DbConnection { fn query(&self) -> DbResult { / … _/ } }

Your database does not support concurrent queries from the same connection. Which of the following marker traits should DbConnection implement?

Chapter 17

OO Features

Chapter 18

Patterns and Matching

Chapter 18

Patterns and Matching