7 - 14 The Rust Programming Language Flashcards

(197 cards)

1
Q

What is a “Package” in Rust?

A

A Cargo feature that lets you build, test, and share crates. It can contain multiple binary crates and optionally one library crate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

What is a “Crate” in Rust?

A

A tree of modules that produces either a library or an executable.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is a “Path” in the context of Rust code organization?

A

A way of naming a specific item, such as a struct, function, or module, so the compiler can locate it.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

What defines a “Binary Crate”?

A

It is a crate that compiles into an executable program and must contain a main function.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What defines a “Library Crate”?

A

It is a crate that does not have a main function and does not compile to an executable; it defines shared functionality for other projects to use.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What is the maximum number of library crates a single package can contain?

A

A package can contain at most one library crate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

How many binary crates can a single package contain?

A

A package can contain as many binary crates as you like.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

If a package contains src/main.rs, what does Cargo assume?

A

Cargo assumes it is the crate root of a binary crate having the same name as the package.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

If a package contains src/lib.rs, what does Cargo assume?

A

Cargo assumes it is the crate root of a library crate having the same name as the package.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Where should you place source files if you want a package to contain multiple binary crates?

A

You should place them in the src/bin directory; each file there becomes a separate binary crate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

When compiling a crate, where does the compiler look for code first?

A

It starts at the crate root file (usually src/lib.rs for libraries or src/main.rs for binaries).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

If you declare mod garden; in the crate root, where does the compiler look for the code?

A
  1. Inline (inside {} replacing the semicolon)2. In src/garden.rs3. In src/garden/mod.rs
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

If you declare mod vegetables; inside src/garden.rs, where does the compiler look for the submodule’s code?

A
  1. Inline (inside {})2. In src/garden/vegetables.rs3. In src/garden/vegetables/mod.rs
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

What is the “module tree”?

A

The hierarchical structure of modules in a crate, starting from the implicit module named crate at the root, similar to a filesystem directory tree.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

What are the two forms of paths you can use to refer to an item in a Rust module tree?

A
  1. Absolute path: Starts from the crate root.2. Relative path: Starts from the current module.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

How does an absolute path begin when referring to code within the current crate?

A

It begins with the literal crate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

How does a relative path begin?

A

It begins with self, super, or an identifier in the current module.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

What is the default privacy setting for items (functions, structs, enums, modules) in Rust?

A

They are private to their parent modules by default.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

What does the super keyword allow you to do in a path?

A

It allows you to start a relative path from the parent module (similar to .. in a filesystem).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

If you make an enum public with pub, are its variants automatically public?

A

Yes, if an enum is public, all of its variants are automatically public.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Why is it beneficial to move most logic into the library crate rather than the binary crate?

A

It allows external projects to share and reuse the functionality, whereas code locked inside a binary crate cannot be easily reused by others.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

In a package with both binary and library crates, where should the module tree be defined?

A

It should be defined in src/lib.rs.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

How does the binary crate access code from the library crate within the same package?

A

It accesses the library using the package name (starting paths with the package name), treating the library exactly like an external crate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

How does separating the binary and library help with API design?

A

It forces you to act as a client of your own code, ensuring the public interface is well-designed and usable before other users try it.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
When bringing a function into scope with use, what is the idiomatic way to do it?
It is idiomatic to bring the parent module into scope (e.g., use crate::front_of_house::hosting) and then call the function using the module name (e.g., hosting::add_to_waitlist()).
26
When bringing structs, enums, or other items into scope with use, what is the idiomatic way to do it?
It is idiomatic to specify the full path to the item itself (e.g., use std::collections::HashMap).
27
What is the exception to the rule of importing full paths for structs/enums?
If you are bringing two items with the same name into scope, you should import their parent modules (or use an alias) to avoid naming conflicts.
28
How can you resolve a naming conflict when importing two items with the same name?
You can use the as keyword to provide a new local name (alias) for one or both of the types (e.g., use std::io::Result as IoResult).
29
How can you clean up multiple use statements that share a common path prefix?
You can use nested paths by specifying the common part followed by {} containing the varying parts (e.g., use std::{cmp::Ordering, io};).
30
What does use std::io::{self, Write}; do?
It brings both std::io (referenced by self) and std::io::Write into scope in a single line.
31
What does the glob operator (*) do in a use statement?
It brings all public items defined in a path into the current scope (e.g., use std::collections::*;).
32
Does the mod keyword act like an "include" statement found in other languages?
No. The mod keyword declares a module to the compiler once; other files simply refer to that module using paths, rather than "including" it again.
33
Where does the Rust compiler look for the code of a submodule declared inside another module (e.g., mod hosting; inside src/front_of_house.rs)?
It looks in a directory named after the parent module (e.g., src/front_of_house/hosting.rs).
34
What are the two file path styles Rust supports for module definitions?
1. The modern style: src/module_name.rs2. The older (legacy) style: src/module_name/mod.rs
35
Why is the modern file path style (e.g., src/front_of_house.rs) generally preferred over the older style (src/front_of_house/mod.rs)?
The older style can result in many files named mod.rs, which can be confusing when multiple mod.rs files are open in an editor simultaneously.
36
What happens if you use both file styles (module.rs and module/mod.rs) for the same module?
The compiler will issue an error because it doesn't know which file to use.
37
What is a Vec in Rust?
A collection type (vector) that stores multiple values of the same type next to each other in memory.
38
What macro does Rust provide to create a vector with initial values?
The vec! macro (e.g., let v = vec![1, 2, 3];).
39
How do you add elements to a vector?
By using the push method (e.g., v.push(5);). The vector must be mutable (mut).
40
What are the two ways to access an element in a vector?
1. Indexing syntax: &v[2] (panics if out of bounds).2. get method: v.get(2) (returns Option<&T>).
41
What happens if you try to access a non-existent index using v.get(100)?
It returns None without panicking, allowing you to handle the error gracefully.
42
Why can't you add an element to a vector while holding a reference to an item in that vector?
Because adding an element might require reallocating memory to a new location, leaving the old reference pointing to deallocated memory. The borrow checker prevents this.
43
How do you iterate over a vector to modify each element?
Use a mutable reference in the loop and dereference the item: for i in &mut v { *i += 50; }.
44
How can you store values of different types in a single vector?
By defining an enum where variants hold the different types, and then creating a vector of that enum type.
45
What happens to a vector's elements when the vector goes out of scope?
When a vector is dropped, all of its contents are also dropped and cleaned up.
46
What are the two main string types discussed in Rust, and how do they differ?
String (growable, mutable, owned, UTF-8 encoded) and &str (string slice, a reference to UTF-8 encoded string data stored elsewhere).
47
How is a String implemented internally in Rust?
It is a wrapper over a Vec, meaning it is a collection of bytes interpreted as UTF-8 text.
48
What method can you use on any type that implements the Display trait to create a String?
The to_string() method (e.g., "hello".to_string()).
49
What is the difference between push_str and push when updating a String?
push_str appends a string slice (string literal) to the String, whereas push appends a single character (char).
50
When using the + operator to concatenate strings (e.g., s1 + &s2), what happens to ownership?
The + operator takes ownership of the left-hand string (s1), appends the content of the right-hand string (s2), and returns ownership of the result. s1 is no longer valid.
51
Why does Rust prefer format! over the + operator for complex string concatenation?
format! is more readable and does not take ownership of any of its parameters, unlike the + operator.
52
Why does Rust disallow string indexing (e.g., s[0])?
1. Strings are UTF-8, so one character may take multiple bytes.2. Indexing implies O(1) performance, but finding a character in variable-width encoding is O(n).3. It avoids ambiguity about whether to return a byte, a char, or a grapheme cluster.
53
What are the three ways Rust allows you to view the data in a String?
1. As bytes ([u8]).2. As scalar values (char).3. As grapheme clusters (what humans perceive as letters).
54
What happens if you try to slice a string (e.g., &s[0..1]) in the middle of a multibyte character?
The program will panic at runtime because the index is not a valid character boundary.
55
Which method should you use to iterate over the Unicode scalar values (chars) of a String?
The .chars() method.
56
Which method should you use to iterate over the raw bytes of a String?
The .bytes() method.
57
How do you add elements to a HashMap?
Use the insert method (e.g., scores.insert(String::from("Blue"), 10);).
58
What happens to ownership when you insert non-Copy values (like Strings) into a HashMap?
The values are moved into the HashMap, and the HashMap becomes the owner. You cannot use the variables afterwards.
59
How do you safely access a value in a HashMap given a key?
Use the get method, which returns an Option<&V> (Some(&value) if found, None if not).
60
With HashMaps, what is the entry API used for?
It checks whether a key exists in the map and allows you to perform actions based on its presence (like inserting a default value only if the key is missing).
61
With HashMaps, what does the method or_insert do when called on an Entry?
It inserts the specified value only if the key does not already exist. It returns a mutable reference to the value (either the existing one or the newly inserted one).
62
In what order does a for loop iterate over keys and values in a HashMap?
In an arbitrary order.
63
How can you update a value based on its old value (e.g., counting word occurrences)?
Use entry(key).or_insert(0) to get a mutable reference to the value, then dereference it to modify it (e.g., *count += 1).
64
What hashing algorithm does Rust's HashMap use by default?
SipHash, which is designed to provide resistance to Denial-of-Service (DoS) attacks, even though it is not the fastest algorithm available.
65
How can you change the hashing algorithm used by a HashMap?
By specifying a different hasher that implements the BuildHasher trait.
66
What is the panic! macro used for in Rust?
It is used to handle unrecoverable errors by printing a failure message, cleaning up the stack, and quitting the program.
67
What are the two ways a panic can occur in a Rust program?
1. By taking an action that causes code to panic (like accessing an array index out of bounds).2. By explicitly calling the panic! macro.
68
What does "unwinding" mean in the context of a Rust panic?
It means Rust walks back up the stack and cleans up the data from each function it encounters before exiting.
69
What is the alternative to "unwinding" when a panic occurs, and what does it do?
The alternative is aborting, which ends the program immediately without cleaning up memory (leaving it to the OS).
70
Why might a developer choose to "abort" on panic instead of unwind?
Aborting reduces the size of the resultant binary because it removes the cleanup logic.
71
How do you configure a Rust project to abort on panic in release mode?
By adding panic = 'abort' to the [profile.release] section of the Cargo.toml file.
72
How does Rust handle an attempt to access an index beyond the end of a vector (e.g., v[99] when length is 3)?
It causes a panic to prevent a buffer overread (security vulnerability), stopping execution immediately.
73
What environment variable must be set to see the call stack (backtrace) when a panic occurs?
RUST_BACKTRACE (e.g., set RUST_BACKTRACE=1).
74
What is a "backtrace"?
A list of all the functions that have been called to get to the point where an error occurred.
75
When reading a backtrace, how do you locate the source of the problem in your code?
Start from the top of the list and look for the first file path that belongs to your project (code you wrote).
76
What must be enabled for a backtrace to contain function names and line numbers?
Debug symbols (which are enabled by default when using cargo build or cargo run without the --release flag).
77
What is the definition of the Result enum in Rust?
enum Result { Ok(T), Err(E), } It has two variants: Ok(T) for success and Err(E) for failure.
78
In the type Result, what do T and E represent?
T represents the type of the value returned in a success case (Ok).E represents the type of the error returned in a failure case (Err).
79
How can you use a match expression to handle a Result returned by a function?
You match on the variants:1. Ok(value): Extracts the success value.2. Err(error): Extracts the error information to handle it (e.g., panic or recover).
80
How do you distinguish between specific error types (like NotFound) when handling an io::Error?
You call the .kind() method on the error struct, which returns an io::ErrorKind enum variant (e.g., ErrorKind::NotFound).
81
What is unwrap_or_else used for in error handling?
It is a method on Result that allows you to define a closure that executes only if the result is Err, providing a cleaner alternative to nested match expressions.
82
What does the unwrap() method do when called on a Result?
If the Result is Ok, it returns the inner value.If the Result is Err, it calls the panic! macro automatically with a default message.
83
How does expect(msg) differ from unwrap()?
It behaves exactly like unwrap() (returns the value on success, panics on failure), but it allows you to specify a custom error message for the panic, making debugging easier.
84
What does "propagating errors" mean in Rust?
Instead of handling an error inside a function (e.g., panicking), the function returns the error to the calling code, allowing the caller to decide how to handle it.
85
What is the syntax for the ? operator and what does it do?
Placed after a Result value (e.g., File::open("foo")?):1. If Ok, the value inside is returned from the expression.2. If Err, the error is returned early from the whole function.
86
How does the ? operator handle error type conversion?
It uses the From trait (specifically the from function) to automatically convert the error received into the error type defined in the current function's return type.
87
What does the standard library function fs::read_to_string do?
It opens a file, creates a new String, reads the file's contents into that String, and returns it. It effectively combines File::open and read_to_string.
88
What is the primary restriction on where you can use the ? operator?
The ? operator can only be used in functions where the return type is compatible with the value the ? is used on (e.g., returning Result for Result values, or Option for Option values).
89
How does the ? operator behave when used on an Option?
If the value is None, it returns None early from the function.If the value is Some, it extracts the value inside and continues.
90
Can you use the ? operator inside the main function?
Yes, but only if you change the return type of main from () to Result<(), E> (typically Result<(), Box>).
91
What does Box represent when used as a return type?
It represents a "trait object" that can hold any kind of error. This is useful when a function might return different types of errors.
92
What happens when a main function returning Result returns an Err?
The executable exits with a nonzero value (indicating failure), compatible with standard C exit codes.
93
In Rust, what do the pipes |error| represent? Example: .unwrap_or_else(|error| { ... })
They define the parameters for a closure. A closure is an anonymous function. The code inside the pipes is the input, just like arguments in parentheses.
94
In production Rust code, which is generally preferred: unwrap or expect, and why?
expect is preferred. It allows you to provide a specific error message explaining why the operation was expected to succeed. This context makes debugging easier. unwrap only provides a generic error.
95
What is the default recommendation for error handling when defining a function that might fail?
You should generally return a Result to give the calling code options on how to handle the failure (recover or panic).
96
In which three specific coding situations is it often appropriate to panic (using unwrap or expect) rather than return a Result?
1. Examples (to keep code clear).2. Prototype code (before deciding on robust handling).3. Tests (where failure should fail the test immediately).
97
When is it acceptable to use expect on a Result in production code?
When you have logic that ensures the Result will always be Ok, but the compiler cannot understand that guarantee (e.g., parsing a hardcoded string known to be valid).
98
What is a "bad state" in the context of deciding to panic?
A state where an assumption, guarantee, contract, or invariant has been broken (e.g., invalid values, contradictory data) and the code cannot reliably continue.
99
Why is panicking generally preferred over returning an error when a function contract is violated?
A contract violation usually indicates a bug in the calling code that needs to be fixed by a programmer, rather than a runtime error that the code should attempt to handle or recover from.
100
How does Rust's type system reduce the need for verbose runtime error checks?
By defining specific types (like u32 for non-negative numbers or custom structs), the compiler ensures validity at compile time, removing the need to check for those specific invalid states at runtime.
101
In the "Guess" number game example, why is the value field of the Guess struct made private?
To prevent external code from setting value directly. This forces the use of the public new function, which contains the validation logic to ensure the value is always between 1 and 100.
102
Why should code panic if it detects invalid values that could cause security issues (like out-of-bounds memory access)?
Proceeding with invalid values in these cases could expose vulnerabilities; panicking stops the program immediately to prevent security breaches or data corruption.
103
What are "Generics" in Rust?
They are abstract stand-ins for concrete types or other properties, allowing you to write code without specifying the exact types that will be used during compilation.
104
How are generic type parameters similar to function parameters?
Just as function parameters allow code to run on unknown values, generic type parameters allow code to run on unknown types.
105
What is a "Trait" in Rust used for?
Traits are used to define behavior in a generic way. They can constrain a generic type so that it only accepts types that implement a specific behavior.
106
What are "Lifetimes" in Rust?
Lifetimes are a specific variety of generics that give the compiler information about how references relate to each other, ensuring they remain valid.
107
What is the standard naming convention for generic type parameters in Rust?
UpperCamelCase, and typically short, often a single letter like T (short for "type").
108
In Rust, why would the compiler reject a generic function body that attempts to compare two values of type T using the > operator?
Because the compiler cannot guarantee that every possible concrete type substituted for T implements the necessary ordering traits (like std::cmp::PartialOrd).
109
How do you define a Rust struct named Point that holds an x and y coordinate of the same generic type T?
struct Point { x: T, y: T, }
110
If a Rust struct is defined as struct Point { x: T, y: T }, what happens if you try to instantiate it with x as an integer and y as a float?
The code will fail to compile because T must resolve to a single concrete type for that instance (both fields must be the same type).
111
How do you define a Rust struct Point that allows x and y to be of different generic types?
By declaring multiple generic parameters, e.g., struct Point { x: T, y: U, }.
112
In the Rust standard library, how is the Option enum defined using generics to handle any data type?
enum Option { Some(T), None, }
113
When implementing a method for a generic struct Point, where must the generic type be declared to specify it applies to all types?
It must be declared immediately after the impl keyword, e.g., impl Point.
114
In Rust, how can you implement a method on a generic struct Point that exists only for a specific concrete type (e.g., f32)?
By specifying the concrete type in the impl block without declaring a generic parameter: impl Point.
115
Is it possible for a Rust method to use generic type parameters (e.g., ) that are distinct from the struct's own generic parameters?
Yes, method-specific generics are declared in the method signature (after the fn name) and are independent of the struct's generics.
116
What is the runtime performance cost of using generics in Rust compared to using concrete types?
There is zero runtime cost; performance is identical to using concrete types.
117
What is monomorphization in the context of Rust generics?
The compile-time process where the compiler generates specific code for each concrete type used in place of a generic parameter, replacing the generic code with specialized implementations.
118
How does the Rust compiler handle an Option used with both i32 and f64 during monomorphization?
It generates two distinct definitions (effectively Option_i32 and Option_f64) and replaces the generic usages with these specific types.
119
120
What is the primary purpose of a "trait" in the Rust programming language?
To define shared behavior among different types in an abstract way.
121
To call a trait's methods on an instance of a type, what must be brought into scope besides the type itself?
The trait definition.
122
According to the Rust "orphan rule," what is the restriction on implementing a trait for a type?
You can only implement a trait on a type if either the trait or the type (or both) are local to your crate.
123
Why does Rust enforce the "orphan rule" (coherence) regarding trait implementations?
To prevent two different crates from implementing the same trait for the same type, which would cause ambiguity for the compiler.
124
What is the impl Trait syntax for a function parameter named item that accepts any type implementing the Summary trait?
item: &impl Summary
125
How is the impl Trait parameter syntax item: &impl Summary expressed using "trait bound" syntax?
fn name(item: &T)
126
Which syntax must be used if a function requires two parameters to be the exact same concrete type that implements the Summary trait?
Trait bound syntax: fn notify(arg1: &T, arg2: &T)
127
What symbol is used to specify that a generic type must implement multiple traits (e.g., Summary and Display)?
The plus sign (+), such as T: Summary + Display.
128
What Rust clause is used to move complex trait bounds to the end of a function signature to improve readability?
The where clause.
129
What is the primary limitation of using -> impl Trait as a function return type?
The function must return a single concrete type; it cannot return different types (e.g., returning either NewsArticle or SocialPost) based on logic.
130
What is a "blanket implementation" in Rust?
Implementing a trait for any type that already satisfies the implementation of another trait (e.g., impl ToString for T).
131
How can you conditionally implement methods for a generic struct Pair only when the inner type T has specific behaviors?
By placing trait bounds on the impl block, such as impl Pair { ... }.
132
133
Why does Rust require lifetime annotations if every reference already has a lifetime?
Most lifetimes are inferred; annotations are only required when the relationship between multiple lifetimes is ambiguous, allowing the compiler to ensure return values don't outlive their sources.
134
In the following code, why does the borrow checker reject r = &x;?rust { let r; { let x = 5; r = &x; } println!("{}", r); }
Because the subject of the reference (x) has a shorter lifetime ('b) than the reference itself (r, 'a). This prevents a dangling pointer when x is dropped at the end of the inner scope.
135
How do lifetime annotations like <'a> actually affect the execution time or memory duration of a reference?
They do not affect duration. Annotations are purely a contract for the compiler to validate; they describe existing relationships without changing when a variable is dropped.
136
Why does the function signature fn longest<'a>(x: &'a str, y: &'a str) -> &'a str use the same lifetime name 'a for all three parts?
It tells the compiler that the returned reference is guaranteed to be valid only as long as both inputs are valid (constrained by the "smallest" concrete lifetime passed in).
137
Given fn longest<'a>(x: &'a str, y: &str) -> &'a str { x }, why is no annotation required for y?
Because the return value is derived exclusively from x. The lifetime of y has no logical relationship to the validity of the output, so it doesn't need to be tracked in that "contract."
138
Why is it impossible to return a reference to a String created inside a function (e.g., let s = String::from("..."); &s)?
The String is owned by the function and dropped when the scope ends; no lifetime annotation can extend its life, as the underlying memory is deallocated on return.
139
What is the logic behind the "Second Elision Rule" (one input lifetime = all output lifetimes)?
If a function takes only one reference, any reference it returns must logically come from that single input, so the compiler automatically links their lifetimes to reduce boilerplate.
140
In a struct definition struct Excerpt<'a> { part: &'a str }, what is the relationship between the struct instance and the data in part?
The annotation ensures that the Excerpt instance cannot outlive the data being referenced in part. If the source of part is dropped, the struct becomes invalid.
141
Why does the &self parameter in methods simplify lifetime annotations (Third Elision Rule)?
Rust assumes that if a method returns a reference, it is likely borrowing from the object itself (self), so it automatically assigns the lifetime of self to the output.
142
What is the unique characteristic of the 'static lifetime regarding program memory?
'static references point to data that is stored directly in the program's binary (like string literals), meaning they are guaranteed to be available for the entire duration of the execution.
143
Why does Rust require a use super::*; statement inside an inner tests module to access functions defined in the outer module?
Because the tests module is a separate namespace in the module tree; it follows standard visibility rules and cannot see items in its parent scope unless they are explicitly brought into scope.
144
What is the fundamental mechanism Rust's test runner uses to determine that a test function has failed?
The runner monitors the thread in which the test is executing; if the thread panics (either via panic!, failed assertions, or an unhandled error), the runner catches the failure and marks the test as FAILED.
145
In the context of assert_eq!, why must custom structs or enums implement the PartialEq and Debug traits?
PartialEq is required to perform the equality comparison (==), and Debug is required so the test runner can print both values to the console if the assertion fails.
146
Why is using assert_eq!(left, right) generally preferred over assert!(left == right) for validating results?
assert_eq! provides better diagnostics; it captures and prints the specific values of both arguments upon failure, whereas assert! only reports that the boolean expression evaluated to false.
147
Given a test annotated with #[should_panic(expected = "Invalid range")], under what specific condition will the test be marked as FAILED even if the code panics?
The test fails if the panic message does not contain the specified substring ("Invalid range"), ensuring the code panicked for the correct conceptual reason rather than an unrelated error.
148
How does the behavior of the ? operator change the outcome of a test function that returns Result<(), E>?
If an operation returns an Err, the ? operator returns that Err from the function; the test runner interprets any returned Err variant as a test failure, similar to a panic.
149
Why can you NOT use the #[should_panic] attribute on a test function that returns a Result?
It creates a logical contradiction: should_panic expects the thread to terminate abruptly, while returning Result implies the function will complete gracefully and return a value for the runner to inspect.
150
How does Rust's test runner handle multiple tests to ensure that a panic in one doesn't stop the others from running?
It executes each test in its own isolated thread; this prevents a single failure from crashing the entire test suite process.
151
Why must Rust tests be independent and avoid shared state (like writing to the same file) by default?
Because Rust runs tests in parallel using threads; if tests modify a shared environment simultaneously, they create race conditions where one test's side effects interfere with another's assertions.
152
What is the conceptual difference between arguments passed before and after the -- separator in cargo test?
Arguments before -- (e.g., cargo test --help) control Cargo's orchestration of the build, while arguments after -- (e.g., cargo test -- --help) are passed directly to the resulting test binary to control its execution behavior.
153
How can you force Rust to execute tests one at a time, and why would you do this?
Use cargo test -- --test-threads=1; this is used when tests share global state or hardware resources that cannot be safely accessed by multiple threads at once.
154
By default, why does println! output only appear for FAILED tests in Rust?
The test runner captures standard output to keep the console clean; it only displays that captured output if a test fails to help you debug the specific state that caused the panic.
155
What is the mental model for how Rust filters tests when you run cargo test ?
Rust performs a substring match against the fully qualified name of the test; any test whose name (including its module path) contains that string will be executed.
156
Why is the #[ignore] attribute used for "expensive" tests rather than just commenting them out?
It keeps the tests compiled and checked for syntax/type errors while allowing you to exclude them from the fast feedback loop of development, requiring an explicit --ignored flag to run.
157
How would you run all tests in a specific module named auth_tests without running the rest of the suite?
Run cargo test auth_tests; since the module name is part of the test's full path, the substring filter will match and execute every function within that specific module.
158
If you want to see println! messages for a passing test to verify logic, which command should you use?
cargo test -- --show-output; this tells the test binary to disable output suppression even for tests that exit successfully.
159
Why is the #[cfg(test)] attribute specifically required for unit tests located in src/lib.rs but not for integration tests in the tests/ directory?
Unit tests live in the same file as production code, so #[cfg(test)] prevents them from being included in the final compiled artifact; integration tests are in a separate directory that Cargo automatically excludes during standard builds.
160
In terms of the "User Perspective," what is the fundamental difference between a unit test and an integration test in Rust?
Unit tests act as internal observers that can access private implementation details, while integration tests act as external consumers, restricted to the library's public API (pub items) just like a third-party crate.
161
Why does Rust allow a tests module to call private functions like internal_adder from its parent module, even if they aren't marked pub?
Because in Rust's visibility system, child modules (like tests) are permitted to see and use any items defined in their ancestor modules, preserving encapsulation from the outside world while allowing internal verification.
162
What happens conceptually when you create a file named tests/integration_test.rs?
Cargo compiles that specific file as a separate, individual crate; this forces you to import your own library (e.g., use my_crate::...) to test it from the perspective of an end user.
163
Why does naming a helper file tests/common/mod.rs prevent it from appearing as a blank entry in the cargo test output?
Files directly in tests/ are treated as test crates to be executed; by placing code in a subdirectory with a mod.rs, you signal to Cargo that this is a module to be used by other tests, not a standalone test suite.
164
If a unit test fails during a full cargo test run, why won't you see the results for your integration tests?
Rust's test runner stops the pipeline for that specific crate's targets; if the unit test phase fails, it assumes the foundation is broken and does not proceed to exercise the more complex integration or doc-test phases.
165
What is the logic behind using use super::*; inside a unit test module?
It allows the test module to bring the parent's environment into its own scope, making functions and structs defined in the parent file accessible without needing a fully qualified path for every call.
166
What is the primary trade-off when using .clone() to resolve ownership issues in a configuration struct?
You trade runtime performance (memory allocation and copying time) for code simplicity and developer productivity by avoiding explicit lifetime annotations.
167
Why is the function Config::build preferred over Config::new when the logic involves potential failure (e.g., missing arguments)?
In Rust idiomatic practice, new is expected to succeed; build (or try_new) signaling a Result allows the caller to handle errors gracefully without a mandatory panic!.
168
Why is the ? operator used in let contents = fs::read_to_string(config.file_path)?; instead of .expect()?
The ? operator propagates the error to the caller for centralized handling, whereas .expect() would trigger a panic! and terminate the program immediately.
169
What is the mechanical reason for returning Ok(()) at the end of the run function?
To satisfy the Result return type when the function completes successfully, indicating it was called for its side effects (like printing) rather than a return value.
170
Why move the logic from main.rs to lib.rs if the binary's behavior remains identical?
Code in lib.rs can be externally tested via integration tests and reused by other crates, whereas code residing solely in main.rs cannot be easily imported or tested.
171
Why can a closure like || self.most_stocked() access self even though it isn't passed as an explicit parameter to the closure?
Closures have the unique ability to capture their environment, meaning they automatically create a record of variables (like self) available in the scope where they are defined.
172
Why does Rust require type annotations for functions (fn) but usually allows them to be omitted for closures?
Functions are part of an explicit public interface where strict types ensure a contract between caller and callee; closures are typically local and anonymous, allowing the compiler to infer types from their specific, narrow usage.
173
In the snippet let example_closure = |x| x;, why does calling example_closure(5) fail if example_closure(String::from("hi")) was called previously?
The compiler infers one concrete type for a closure's parameters upon the first call; once it infers String, the closure is "locked" to that type and cannot be reused with an integer.
174
In the snippet let c = || println!("{:?}", list);, why does the closure capture list as an immutable reference rather than taking ownership?
Rust closures analyze the body and capture the least restrictive access required; since println! only needs to read the data, the closure defaults to an immutable borrow (&T).
175
What is the mechanical purpose of the move keyword in the snippet thread::spawn(move || println!("{:?}", list))?
It forces the closure to take ownership of list (T) instead of borrowing it, ensuring the data remains valid even if the original scope (the main thread) finishes and drops the variable.
176
Why does the FnOnce trait apply to all Rust closures?
Because every closure, regardless of how it captures data, is guaranteed to be callable at least once. FnOnce is the most inclusive "base" trait for all closure behaviors.
177
Why is unwrap_or_else defined with the FnOnce trait bound, while sort_by_key requires FnMut?
unwrap_or_else calls its closure at most once (only if the variant is None), whereas sort_by_key must call the closure multiple times (once for every element in the slice), necessitating a trait that allows repeated calls.
178
What happens to the "capture mode" if a closure body neither mutates nor moves a captured variable?
The closure will implement the Fn trait, representing the most restrictive (and thus most flexible for the caller) capture mode: an immutable borrow.
179
What is the "mechanical why" behind Rust iterators being categorized as zero-cost abstractions?
Rust's compiler performs aggressive optimizations, such as loop unrolling and the elimination of bounds checking, which translates high-level iterator code into machine instructions equivalent to hand-optimized assembly.
180
In the context of Rust performance, what does the principle "What you don't use, you don't pay for" mean?
It means the language doesn't include a heavy runtime or "hidden" overhead for features; if you don't use iterators, your binary doesn't carry extra weight, and if you do, they are as efficient as manual loops.
181
Why might an iterator-based implementation of a search function be slightly faster or equal to an explicit for loop in benchmarks?
Iterators often allow the compiler to better optimize memory access patterns and safely bypass certain safety checks (like array bounds checking) that might be repeated in a manual index-based loop.
182
Why should a Rustacean choose iterators over manual loops even when performance is identical?
Iterators offer a higher-level abstraction that improves code readability and expressiveness (functional style) while maintaining low-level performance, reducing the surface area for manual logic errors.
183
How does the Rust compiler handle the closures used within iterator adapters (like map or filter) during compilation?
The compiler inlines the closure's logic directly into the iteration code, effectively removing the function call overhead and allowing for the same optimizations as a standard loop body.
184
Why does Rust provide distinct dev and release profiles instead of a single compilation mode?
It allows developers to balance the trade-off between compilation speed and execution speed; dev prioritizes fast feedback during coding, while release prioritizes maximum performance for the end user.
185
In the context of opt-level, what is the mechanical trade-off being made when moving from level 0 to level 3?
Higher opt-level values trigger more complex compiler analysis and transformations, which results in significantly longer compile times but produces more efficient machine code.
186
Why is opt-level = 0 the default for the dev profile?
To minimize the time spent in the "Compile" phase of the development cycle, ensuring that programmers can test changes as quickly as possible without waiting for heavy optimizations.
187
If you add [profile.dev] opt-level = 1 to your Cargo.toml, what happens to the other settings for the dev profile?
Only the specified setting is overridden; all other settings (like debug information) remain at their predefined dev defaults.
188
Why is it considered acceptable for a release build to have a much longer compilation time than a dev build?
Because a release build is typically compiled once and then distributed to run many times; the one-time "cost" of long compilation is amortized over the program's entire lifespan.
189
What does the [unoptimized + debuginfo] tag in a cargo build output signify about the binary?
It indicates the binary was built using the dev profile, meaning it lacks performance optimizations but includes metadata that makes it easier to debug logic errors.
190
What is the purpose of "yanking" a crate version with cargo yank --vers 1.0.1?
It prevents new projects from adopting a broken or buggy version while allowing existing projects (with a Cargo.lock) to continue functioning without interruption.
191
Which two metadata fields are strictly required by Cargo before a crate can be published to Crates.io?
A description (to explain the crate's purpose) and a license (using an SPDX identifier like MIT or Apache-2.0) are mandatory for all public crates.
192
Why does a Cargo workspace use a single top-level target directory instead of individual directories for each member?
To prevent redundant recompilation; since workspace members often depend on each other, a shared output directory allows them to reuse existing compiled artifacts.
193
What is the mechanical benefit of having only one Cargo.lock file at the workspace root?
It ensures dependency consistency; all crates in the workspace are forced to use the same version of shared external dependencies (like rand), guaranteeing compatibility across the whole project.
194
Why is it useful to split a large project into multiple library crates within a workspace?
It promotes modular logic and faster incremental builds; by breaking "one big blob" into smaller, self-contained components, you make the code easier to test and reason about.
195
What is the closure type Fn and what is its use case?
Mechanism: It captures variables by immutable reference (&self). Because it only reads data, it can be called repeatedly and safely shared across threads. Use Case: Read-only operations where the closure might be called multiple times or concurrently. Example: Iterator::filter (checks a value without changing it) or Arc (shared callbacks in multithreaded contexts).
196
What is the closure type FnMut and what is its use case?
Mechanism: It captures variables by mutable reference (&mut self). Because it modifies its environment, it requires exclusive access and cannot be called concurrently by multiple threads. Use Case: Operations that need to update internal state across multiple calls. Example: A closure passed to Iterator::map that increments a running total or modifies a captured buffer.
197
What is the closure type FnOnce and what is its use case?
Mechanism: It captures variables by value (taking ownership/self). Because it moves variables out of its environment (consumes them), it can be called exactly once. Use Case: Operations that must transfer ownership or destroy the captured data. Example: thread::spawn (moves data to the new thread to ensure safety) or an API that consumes a builder object to produce a final result.