← Back to quizzesFree quiz

Fundamentals of Rust Programming

Rust is a systems programming language that emphasizes safety, speed, and concurrency. This course extracts the core concepts tested in a typical introductory quiz, turning each question…

11 questions~6 min
Fundamentals of Rust Programming — Qwi
0 / 11
Score: 0%
1

What is the default mutability of a variable declared with `let x = 3;` in Rust?

2

Which macro should be used to print a formatted string to standard error in Rust?

3

Given `let array: [u32; 5] = [0, 1, 2, 3, 4];`, what is the type of `array`?

4

What does the `?` operator do in a function returning `Result`?

5

Which of the following correctly creates an instance of `Point` using the associated function `new`?

6

What will the following code print? ```rust let value: Option = Some(12); if let Some(v) = value { println!("There is {v}"); } ```

7

Why does the following Rust code cause a panic? ```rust let array = [0 ; 10]; for i in 0..20 { print!("{} ", array[i]); } ```

8

In a `match` expression, what does the `_` pattern represent?

9

What is the purpose of the `Result` type in Rust?

10

Which statement correctly describes Rust's approach to undefined behavior (UB) compared to C?

11

What does the `mut` keyword allow when declaring a variable in Rust?

Introduction to Rust Fundamentals

Rust is a systems programming language that emphasizes safety, speed, and concurrency. This course extracts the core concepts tested in a typical introductory quiz, turning each question into a learning module. By the end of the lesson you will understand variable mutability, macro usage, array types, error handling with the ? operator, associated functions, pattern matching, and common runtime pitfalls.

1. Variable Mutability in Rust

When you declare a variable with let, Rust treats it as immutable by default. This design forces developers to think about state changes explicitly, reducing accidental bugs.

Key Points

  • Immutable variables cannot be reassigned after their initial binding.
  • To make a variable mutable, prepend the mut keyword: let mut x = 3;.
  • Immutability applies to the binding, not the underlying data. For example, a mutable reference to an immutable variable can still modify the data it points to.

Understanding this rule helps you write clearer, more predictable code and aligns with Rust’s ownership model.

2. Printing to Standard Error with Macros

Rust provides separate macros for writing to standard output (stdout) and standard error (stderr). The macros print!() and println!() target stdout, while eprint!() and eprintln!() target stderr.

When to Use eprint!() vs eprintln!()

  • eprint!() writes a formatted string to stderr without appending a newline.
  • eprintln!() does the same but automatically adds a newline, mirroring the behavior of println!() for stdout.

Mnemonic: The leading “e” stands for **error**, reminding you that these macros are for error streams.

3. Fixed‑Size Arrays and Their Types

Rust distinguishes between fixed‑size arrays and dynamically sized collections like Vec. The syntax [T; N] denotes an array of N elements of type T. For example:

let array: [u32; 5] = [0, 1, 2, 3, 4];

Here array has the concrete type [u32; 5]. This type encodes the length at compile time, enabling the compiler to perform bounds checking and optimizations.

Why Not Vec?

  • Vec is heap‑allocated and can grow or shrink at runtime.
  • Fixed‑size arrays live on the stack and have a known size, which can be more efficient for small collections.

4. The ? Operator for Error Propagation

In functions that return Result, the ? operator provides concise error handling. When applied to a Result value, it does the following:

  • If the value is Ok(v), it yields v and execution continues.
  • If the value is Err(e), the function returns early with Err(e), propagating the error to the caller.

This operator eliminates the need for explicit match statements, making code easier to read while preserving Rust’s explicit error handling philosophy.

Example

fn read_file(path: &str) -> Result<String, std::io::Error> {
    let mut file = std::fs::File::open(path)?; // Propagates Err automatically
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

5. Associated Functions and Constructors

Rust structs can define associated functions—functions that belong to the type rather than an instance. The most common pattern is a new constructor.

Correct Syntax

To call an associated function, use the double‑colon syntax followed by parentheses:

let p = Point::new();

Incorrect attempts such as let p = Point::new (missing parentheses) or let p = new::Point() (invalid namespace) will not compile.

6. Pattern Matching with if let

The if let construct provides a concise way to match a single pattern while ignoring the rest. Consider the following code:

let value: Option<u32> = Some(12);
if let Some(v) = value { println!("There is {v}"); }

Because value matches Some(v), the block executes and prints There is 12. The placeholder v binds the inner value, allowing it to be used inside the block.

7. Runtime Panics: Out‑of‑Bounds Access

Rust guarantees memory safety by performing bounds checks on array indexing at runtime. Attempting to access an element outside the valid range triggers a panic.

Illustrative Example

let array = [0; 10];
for i in 0..20 {
    print!("{} ", array[i]);
}

The loop iterates from 0 to 19, but the array only contains indices 0‑9. When i reaches 10, the program panics with a message similar to:

thread 'main' panicked at 'index out of bounds: the len is 10 but the index is 10'

To avoid this, ensure loop bounds match the collection size or use safe iteration methods like for element in &array.

8. The Wildcard Pattern _ in match

In a match expression, the underscore (_) acts as a catch‑all pattern. It matches any value that hasn't been matched by previous arms, ensuring the match is exhaustive.

Example

match number {
    0 => println!("Zero"),
    1 => println!("One"),
    _ => println!("Other"), // Handles all remaining cases
}

While the underscore discards the matched value, you can also bind it to a variable using _ followed by a name (e.g., _unused) if you need the value later.

Conclusion and Further Study

These eight modules cover the foundational concepts that every Rust beginner should master. By internalizing variable mutability, macro distinctions, array typing, the ? operator, associated functions, pattern matching, and safety checks, you’ll be prepared to write robust, idiomatic Rust code.

To deepen your knowledge, explore the following topics:

  • Ownership, borrowing, and lifetimes.
  • Advanced error handling with Result combinators.
  • Concurrency primitives such as std::thread and async/await.
  • Trait implementations and generics for code reuse.

Happy coding, and remember: Rust’s safety guarantees are strongest when you let the compiler guide you through explicit, well‑structured code.