Quizfragen zu “The Book”

Systemnahe Programmierung mit Rust

The Book: https://doc.rust-lang.org/book

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

H. Högl, 📧

Letzter Update: 2024-01-18

Kap. 1: Fragen zu cargo, rustup, rustc

  1. Wie legt man ein neues Projekt P an?

    1. cargo create P
    2. cargo make P
    3. cargo new P
    4. cargo build
  2. Welche Dateien wurden generiert, nachdem man mit cargo ein neues Projekt erzeugt, aber noch nicht gebaut hat?

  3. Wie nennt man das Format des Cargo Konfigurationsfiles?

  4. Wo muss sich die Datei ex1.rs befinden, damit das folgende Kommando funktioniert:

    cargo run --example ex1
    1. In Projekt/src/ex1.rs
    2. In Projekt/bin/ex1.rs
    3. In In Projekt/examples/ex1.rs
  5. Module kann man auch in src/bin/ ablegen. Wie kann man diese zu ausführbaren Dateien kompilieren?

  6. Wie legt man ein Bibliotheks-Crate mit cargo an?

  7. Was gibt cargo --list aus?

    1. Den Quelltext
    2. Eine Liste aller verfügbaren cargo Kommandos
    3. Alle Dateien im aktuellen Verzeichnis
  8. Wie kann man herausfinden, ob es ein Update der Rust Toolchain gibt?

  9. Wie installiert man ein Update der Rust “stable” Toolchain?

  10. Sie möchten ein kurzes eigenständiges Programm in der Quelltextdatei programm.rs direkt mit dem Rust Compiler kompilieren und ausführen. Welche Schritte auf der Kommandozeile sind nötig?

  11. Nennen Sie zwei verschiedene Kommandozeilenaufrufe, um den Rust Quelltext zu formatieren.

  12. Wie macht man einen “Release-Build”?

    1. cargo release
    2. cargo build --release
    3. CARGO_RELEASE=1 cargo build
  13. Wie findet man heraus, welche Rust Toolchains installiert sind und welches die gerade aktive Toolchain ist?

Kap. 2: Guessing Game

  1. Wie nennt man “Funktionsaufrufe”, die ein Ausrufezeichen ! am Ende haben, z.B. println!() oder format!()?

    1. Closures
    2. Macros
    3. Inline Funktionen
  2. Wie nennt man die Art Funktion, die wie im folgenden Beispiel durch :: auf einem Typ (String) aufgerufen wird?

    let mut guess = String::new();
  3. Wie liest man ganz elementar von der Standardeingabe einen String ein?

  4. Ist die Erzeugung von Zufallszahlen in der Rust Standardbibliothek oder benötigt man eine externe Bibliothek (Crate), wenn ja welche?

  5. Wozu dient die Cargo.lock Datei?

  6. Wie kann man prinzipiell die Abhängigkeiten eines Programms auf neuere Versionen aktualisieren?

  7. Wie prüft man in Rust typischerweise eine Variable, ob sie einen von mehreren vorgegebenen Werten hat? Man kann auch sagen, man prüft, ob ein bestimmtes Muster erfüllt wird.

Kap. 3: Common Programming Concepts

  1. Aussage: Falls ein Array maximal 256 Elemente gross ist, dann kann ich es mit einem Index vom Typ u8 indizieren.

  2. Welche Bedeutung hat der Typ usize?

  3. Wie viele Bytes umfasst der “char” Datentyp?

  4. Wie schreibt man ein Literal vom char Typ?

  5. Kann ich eine Variable i vom Datentyp i32 in einen Typ f32 umwandeln?

  6. Welches Ergebnis kommt bei 72 / 10 in Rust heraus?

  7. Welcher Datentyp wird bei folgender Zeile voreingestellt für f verwendet?

    let f = 3.14;
  8. Geben Sie drei verschiedene Schreibweisen an, wie man die folgende Zeile ändern kann, so dass explizit der Typ u8 für j verwendet wird.

    let j = 255;

    let j : u8 = 255; let j = 255u8; let j = 255_u8; –>

  9. Wie kann man in Rust numerische Werte lesbarer schreiben?

  10. Ordnen Sie die Datentypen zu:

    
    let u = ();
    let w = "Hallo";
    let x = [4, 9, 1, 0, 6, 2];
    let y = (4, 9, 1, 0, 6, 2);
    let z = &x[2..4];
    1. Array : _________
    2. Slice : _________
    3. Tuple : _________
  11. In welchem Speicherbereich liegen die folgenden Variablen und Konstanten?

    let u = 42;
    let v = [3, 2, 1];
    let w = vec![3, 2, 1];
    let x = "Hallo".to_string();
    let y = &x;
    let z: &'static str = "Rust";
    static S: u64 = 1;
    static mut G: u32 = 42;
    const C: i32 = 42;
    1. Stack: _____________
    2. Heap: _____________
    3. data/bss section: _____________
    4. text section (read-only): _____________
    5. Nirgends: ____________
  12. Was passiert bei dem folgenden Programm?

    fn main() {                                                                     
        let v: Vec<i32> = vec![];
        let i = v[0];
    }                                                                               
    1. Compiler bricht mit einer Fehlermeldung ab
    2. Zur Laufzeit bricht das Programm mit einer Fehlermeldung ab (panic)
    3. Der Wert v[0] wird in i kopiert
    4. In i ist zum Schluss ein Err(None) enthalten
  13. Welcher Typ wird von v.get(n) zurückgeliefert, wenn v ein Vektor vom Typ Vec<u64> ist. n ist vom Typ usize.

    1. Der Typ u64
    2. Der Typ Result<T, E>
    3. Der Typ Option<T>
    4. Der Typ Some(u64)
  14. Welche der folgenden Datentypen können Elemente mit unterschiedlichen Typen beinhalten?

    1. Arrays
    2. Tuples
    3. Structs
    4. Tuple-Structs
    5. Vector
  15. Was wird ausgegeben?

    let hello = "Здравствуйте";
    println!("{:?}", hello.chars().nth(1).unwrap());
  16. Aus welchen 3 Bestandteilen besteht s?

    let s: String::from("Augsburg");
  17. Aus welchen 2 Bestandteilen besteht t?

    let s: String::from("Augsburg");
    let t: &s[4..];
  18. Welchen Wert hat die Variable x nach der Zeile (2) und nach der Zeile (3)?

    let gerade = true;
    let x = if gerade { 2 } else { 3 };  // (2)
    let x = if gerade { 2; } else { 3; };  // (3)
  19. Als Platzhalter bei print-Anweisungen wird oft einer der Folgenden verwendet: {}, {:?}, {:#?}. Wie unterscheiden sie sich?

  20. Kann man in Rust eine globale Konstante definieren, z.B. let PI = 3.1415;. Wie müsste man die Zeile schreiben, damit es funktioniert?

  21. Auf Typen wie u8 kann man durch :: getrennt Funktionen aufrufen, z.B.:

    u8::max_value()

    Welche Möglichkeiten gibt es, um solche Funktionen für eigene Typen zu definieren?

  22. Was muss an den folgenden Stellen eingetragen werden, damit das Beispiel funktioniert?

    let ___ a : i32 = 42;
    
    fn add_one_ref(arg : ___ i32) -> () {
        *arg += 1;
    }
    
    add_one_ref(___ a);
    println!("{}", a);
  23. Kann man einfach schreiben “if zahl {…}”, so wie in C, d.h. haben die Zahlen auch boolsche Wahrheitswerte?

  24. Muss die Schleifenvariable i in Zeile (1) definiert werden oder kann man auf diese Zeile verzichten?

    let mut i = 0;  // (1)
    for i in 0..6 {
        println!("{}", i);
    }
  25. Welche Kommentare können in einem Programm vorkommen?

       // Kommentar a
    
       # Kommentarzeile b
    
       /* Kommentar c */
    
       /*
           /* Kommentar d */
        */
  26. Wie viele Leerzeichen werden typischerweise bei der Formatierung des Quelltextes für eine Einrückung verwendet?

  27. Wenn ein Block nur eine Zeile lang ist, darf man dann die Klammern weglassen, so wie in C?

    let b = 1;
    if b 
        println!("b is true"0);
  28. Wie nennt man Zeilen im Programmtext, die wie in folgendem Beispiel aussehen:

    #[allow(unused_variables)]
    1. Kommentare
    2. Attribute
    3. Präprozessor Anweisungen

Kap. 4: Ownership

  1. Nennen Sie die drei Ownership-Regeln.

  2. Nennen Sie die Regeln für Referenzen.

  3. In einem Code-Abschnitt möchte ich folgende zwei Zeilen nacheinander ausführen. Die Variable r ist der Rückgabewert von std::io::stdin().read_line(&mut guess):

    println!("{:?}", r.expect("Fehler aufgetreten."));  
    println!("{:?}", r.unwrap());   

    Wird das so klappen?

Kap. 5: Struct

  1. Wie nennt man folgende Strukturdefinition?

    struct Point(i32, i32);
    1. Tuple
    2. Tupel Struct
    3. Struct Array
  2. Ist eine leere Strukturdefinition erlaubt?

    struct Empty;
    1. Ja, das ist die Unit Struct
    2. Nein, Strukturen müssen Felder mit oder ohne Namen haben
  3. Definieren Sie auf der Struktur Rechteck eine Methode flaeche(???) -> ???, welche die Fläche des Rechtecks zurückgibt. Den Übergabeparameter und den Rückgabewert wählen Sie sinnvoll (siehe Platzhalter ???). Erzeugen Sie eine Instanz r des Rechteck und rufen Sie die Methoden auf.

    struct Rechteck {
        breite: f32,
        hoehe: f32
    }

Kap. 6: Enums and Pattern Matching

  1. Wie kann man den folgenden Code kürzer schreiben?

    let config_max = Some(3u8);
    match config_max {
        Some(max) => println!("The maximum is configured to be {}", max),
        _ => (),
    }

Kap. 7: Packages, Crates, Modules

  1. Markieren Sie die richtigen Aussagen über ein Package.

    1. Enhält eine Cargo.toml Datei.
    2. Enthält beliebig viele ausführbare Crates.
    3. Enthält beliebig viele Bibliotheks-Crates.
    4. Enthält maximal ein Bibliotheks-Crate.
    5. Enthält maximal ein ausführbares Crate.
  2. –> Was macht das super Schlüsselwort?

    1. Geht zum höchsten Punkt im Modulbaum.
    2. Geht zum Elternmodul des aktuellen Moduls.
  3. Markieren Sie alle Möglichkeiten, wie f2() in der Zeile (x) aufgerufen werden kann.

    mod omod {
    
        fn f2() {
            // ...
        }
    
        pub mod imod {
            pub fn f() {
                // (x) 
            }
        }
    }
    1. omod::f3();
    2. super::f3();
    3. crate::omod::f3();
  4. In einem Unit Test test1() möchten Sie direkt f1() ausführen. Welche der use Anweisungen an der Stelle (x) sind richtig?

    fn f1() { ... }
    
    #[cfg(test)]
    mod tests {
        // (x)
    
        #[test]
        fn test1() {
             f1();
        }
    }
    1. use crate::f1
    2. use super::f1
    3. use super::*
  5. Wenn man die beiden folgenden use Anweisungen verwendet, dann ist der Name Event nicht mehr eindeutig. Welche der folgenden Aussagen sind richtig?

    use crate::mod1::Event;
    use crate::mod2::Event;
    1. use crate::mod1::Event as Event1;
    2. Zwei gleiche Namen in unterschiedlichen Modulen sind nicht erlaubt
    3. use crate::mod1 und dann mod1::Event verwenden
  6. Wie fügt man einem Package das externe Crate rand als Abhängigkeit hinzu? Welche Aussagen sind wahr?

    1. Cargo.lock um rand erweitern
    2. rustup install rand
    3. Abschnitt [dependencies] in Cargo.toml erweitern um rand = "0.5";

Kap. 8: Collections

  1. Wie kann man einen neuen Vektor mit Werten vorbelegen und einer Variablen zuordnen?

  2. Macht es Sinn, wenn man einen String mit einem numerischen Wert indiziert um damit das Zeichen an der entsprechenden Stelle zu bekommen? Mit kurzer Begründung.

  3. Wie iteriert man durch einen String?

Kap. 9: Errors

  1. Was passiert beim Aufruf von panic!("Hallo Leute")?

    1. Das Programm wird abgebrochen und der Argumentstring wird ausgegeben.
    2. Das Programm geht in eine Endlosschleife.
    3. Das Programm wird vom Betriebssystem abgeschossen.
  2. Was passiert wenn bei der Ausführung der Funktion read_file() die Datei testfile.txt nicht vorhanden ist?

    fn read_file() -> Result<(), io::Error> {                                       
        let f = File::open("testfile.txt");
        Ok(())
    }
    1. Ein panic!() wird erzeugt.
    2. Die Funktion read_file() kehrt mit einem io::Error() zurück.
    3. In f ist eine Err(...) Instanz und die read_file() kehrt mit Ok(()) zurück.
    4. Eine Exception wird ausgelöst.
  3. Wie lassen sich die folgenden mit (x) markierten Zeilen einsparen?

    fn read_file() -> Result<(), io::Error> {
        let f = File::open("testfile.txt");
        let f = match f {           // (x)
            Err(_) => panic!(),     // (x)
            Ok(f) => f,             // (x)
        };                          // (x)
    ...
  4. Nennen Sie eine Varianten von unwrap(), die keine panic! auslöst, sondern eine Closure als Argument erwartet, die im Fehlerfall ausgeführt wird.

  5. Welche Aussagen zum ? Operator sind richtig?

    1. Im Fehlerfall wird die umschliessende Funktion beendet.
    2. Der ?-Operator funktioniert nur bei Result<T, E>, nicht bei Option<T>.
    3. Im Fehlerfall wird automatisch die from() Funktion im From Trait aufgerufen, um die Fehlertypen bei Bedarf anzupassen.
    4. Der ?-Operator erzeugt eine panic!, wenn der zurückgegebene Fehler nicht mit dem Rückgabewert der umschliessenden Funktion zusammenpasst.

Kap. 10: Generics, Traits, Lifetimes

  1. Was können Trait-Definitionen enthalten?

Kap. 11: Tests

  1. Wie kann man Tests mit cargo und rustc ausführen?

  2. Kopieren Sie den folgenden Code in das Textfeld und füllen Sie die punktierten Bereich mit funktionierendem Code aus.

    #[........]
    mod tests {
        use ........;
    
        #[.....]
        fn cnt_test() {
            let sum = cnt(); // sum expected to be 42
            assert_eq!(...., ....);
        }
    }                           

Kap. 12: CLI Tool

Kap. 13: Iterators, Closures

  1. Schreiben Sie eine Closure mit Argument x und geben Sie als Rückgabewert x+1 zurück. Weisen Sie diese Closure einer Variablen c zu.

  2. Wie nennt man das Konstruct das als Argument von dem folgenden unwrap_or_else() verwendet wird:

    unwrap_or_else(|error| { panic!("...") });
  3. Nennen Sie drei unterschiedliche Methoden um über einen Vektor zu iterieren.

  4. Nennen Sie drei Iterator Adaptoren ihrer Wahl

  5. Nennen Sie drei Iterator Konsumenten ihrer Wahl

  6. Wie würden Sie einen Iterator für einen selbstgemachten Datentyp definieren? Nur in Stichpunkten.

  7. Welcher Wert kommt bei der folgenden Verkettung heraus?

    let c = Counter::new(); // Iterator: 1, 2, 3, 4, 5
    c.zip(Counter::new().skip(1))
     .map(|(a, b)| a * b)
     .filter(|x| x % 3 == 0)
     .sum()

Kap. 15: Smart Pointers

  1. Wie kann man eine Variable der folgenden Datenstruktur auf dem Heap anlegen?

    struct Circle {
        center: (f32, f32),
        radius: f32
    }
    1. let c = Box::new(Circle { center: (1.0, 4.0), radius: 5.0 });

    2. let c = malloc(Circle { center: (1.0, 4.0), radius: 5.0 });

    3. let c = Rc::new(RefCell::new(Circle { center: (1.0, 4.0), radius: 5.0}));

Kap. 16: Concurrency

  1. Benutzt Rust in der Standardbibliothek die Threads des Betriebssystems oder wird ein eigener Thread-Scheduler in der Rust Runtime verwendet?

    1. Betriebssystem-Threads, 1:1
    2. Thread-Scheduler in der Rust Runtime (“green threads”), M:N
  2. Wie kann man einen Thread für eine bestimmte Zeit schlafen legen?

  3. Mit welchen Mitteln können Threads untereinander kommunizieren?

  4. Wofür steht “mpsc”?

  5. Wie kann das Hauptprogramm warten, bis alle vorher erzeugten Threads beendet wurden?

Kap. 17: Object-Oriented Programming

  1. Kann man Methoden nur auf Strukturen (struct) definieren, oder kann man das auf beliebigen Datentypen machen?

    1. Nur auf struct.
    2. Auf beliebigen Datentypen.
  2. Wie wir die in der objektorientierten Programmierung übliche Methodenauswahl zur Laufzeit in Rust erzielt?

    1. Durch Vererbung mit “dynamic dispatch”
    2. Durch Trait Objects
    3. Das ist in Rust nicht möglich
  3. In der objektorientierten Programmierung gibt es Beziehungen zwischen Klassen, so dass eine Instanz der Klasse A (Typ A) auch eine Instanz der Klasse B (Typ B) ist. Wie werden diese Beziehungen in Rust realisiert?

    1. Durch generische Programmierung
    2. Durch Module
    3. Durch Trait Bounds