diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index b68cb1b3..5c423e24 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -23,7 +23,9 @@ jobs:
with:
globs: "exercises/**/*.md"
- name: Run cargo fmt
- run: cargo fmt --all -- --check
+ run: cargo fmt --all --check
+ - name: Run rustfmt on solutions
+ run: rustfmt --check --edition 2021 --color always solutions/**/*.rs
test:
runs-on: ${{ matrix.os }}
strategy:
@@ -33,7 +35,7 @@ jobs:
- uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2
- name: Run cargo test
- run: cargo test
+ run: cargo test --workspace
dev-check:
runs-on: ubuntu-latest
steps:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9025d451..80e000a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,22 @@
+
+
+## 6.1.0 (2024-07-10)
+
+#### Added
+
+- `dev check`: Check that all exercises (including third-party ones) include at least one `TODO` comment.
+- `dev check`: Check that all exercises actually fail to run (not already solved).
+
+#### Changed
+
+- Make enum variants more consistent between enum exercises.
+- `iterators3`: Teach about the possible case of integer overflow during division.
+
+#### Fixed
+
+- Exit with a helpful error message on missing/unsupported terminal/TTY.
+- Mark the last exercise as done.
+
## 6.0.1 (2024-07-04)
@@ -64,7 +83,7 @@ This should avoid issues related to the language server or to running exercises,
Clippy lints are now shown on all exercises, not only the Clippy exercises 📎
Make Clippy your friend from early on 🥰
-### Third party exercises
+### Third-party exercises
Rustlings now supports third-party exercises!
diff --git a/Cargo.lock b/Cargo.lock
index 5da969cc..f3824f16 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
-version = "4.5.8"
+version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
+checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [
"clap_builder",
"clap_derive",
@@ -161,9 +161,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.8"
+version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
+checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [
"anstream",
"anstyle",
@@ -354,15 +354,6 @@ version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
-[[package]]
-name = "itertools"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
-dependencies = [
- "either",
-]
-
[[package]]
name = "itertools"
version = "0.13.0"
@@ -525,7 +516,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.5.2",
+ "redox_syscall 0.5.3",
"smallvec",
"windows-targets 0.52.6",
]
@@ -594,7 +585,7 @@ dependencies = [
"cassowary",
"compact_str",
"crossterm",
- "itertools 0.13.0",
+ "itertools",
"lru",
"paste",
"stability",
@@ -616,9 +607,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
"bitflags 2.6.0",
]
@@ -654,7 +645,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rustlings"
-version = "6.0.1"
+version = "6.1.0"
dependencies = [
"anyhow",
"assert_cmd",
@@ -673,7 +664,7 @@ dependencies = [
[[package]]
name = "rustlings-macros"
-version = "6.0.1"
+version = "6.1.0"
dependencies = [
"quote",
"serde",
@@ -709,18 +700,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
-version = "1.0.203"
+version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.203"
+version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
@@ -785,9 +776,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "stability"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
+checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
dependencies = [
"quote",
"syn",
@@ -829,9 +820,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.68"
+version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
+checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
@@ -855,9 +846,9 @@ dependencies = [
[[package]]
name = "toml_edit"
-version = "0.22.14"
+version = "0.22.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
+checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
dependencies = [
"indexmap",
"serde",
@@ -880,11 +871,12 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-truncate"
-version = "1.0.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
+checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
dependencies = [
- "itertools 0.12.1",
+ "itertools",
+ "unicode-segmentation",
"unicode-width",
]
@@ -1103,9 +1095,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
-version = "0.6.13"
+version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
+checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f"
dependencies = [
"memchr",
]
diff --git a/Cargo.toml b/Cargo.toml
index 22141144..f5fe7380 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ exclude = [
]
[workspace.package]
-version = "6.0.1"
+version = "6.1.0"
authors = [
"Liv ",
"Mo Bitar ",
@@ -20,8 +20,8 @@ license = "MIT"
edition = "2021"
[workspace.dependencies]
-serde = { version = "1.0.203", features = ["derive"] }
-toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] }
+serde = { version = "1.0.204", features = ["derive"] }
+toml_edit = { version = "0.22.16", default-features = false, features = ["parse", "serde"] }
[package]
name = "rustlings"
@@ -47,13 +47,13 @@ include = [
[dependencies]
anyhow = "1.0.86"
-clap = { version = "4.5.8", features = ["derive"] }
+clap = { version = "4.5.9", features = ["derive"] }
crossterm = "0.27.0"
hashbrown = "0.14.5"
notify-debouncer-mini = { version = "0.4.1", default-features = false }
os_pipe = "1.2.0"
ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] }
-rustlings-macros = { path = "rustlings-macros", version = "=6.0.1" }
+rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" }
serde_json = "1.0.120"
serde.workspace = true
toml_edit.workspace = true
diff --git a/README.md b/README.md
index 8c1a101c..a7f81c12 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,21 @@ After installing Rustlings, run the following command to initialize the `rustlin
rustlings init
```
+
+If the command rustlings can't be found… (click to expand)
+
+You are probably using Linux and installed Rust using your package manager.
+
+Cargo installs binaries to the directory `~/.cargo/bin`.
+Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable.
+
+The solution is to …
+
+- either add `~/.cargo/bin` manually to `PATH`
+- or to uninstall Rust from the package manager and install it using the official way with `rustup`: https://www.rust-lang.org/tools/install
+
+
+
Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises:
```bash
diff --git a/exercises/04_primitive_types/README.md b/exercises/04_primitive_types/README.md
index cea69b02..d67668a3 100644
--- a/exercises/04_primitive_types/README.md
+++ b/exercises/04_primitive_types/README.md
@@ -5,5 +5,5 @@ compiler. In this section, we'll go through the most important ones.
## Further information
-- [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html)
-- [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html)
+- [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html)
+- [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html)
diff --git a/exercises/05_vecs/README.md b/exercises/05_vecs/README.md
index 8ff9b85f..e1b61285 100644
--- a/exercises/05_vecs/README.md
+++ b/exercises/05_vecs/README.md
@@ -12,6 +12,6 @@ the other useful data structure, hash maps, later.
## Further information
-- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html)
+- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html)
- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut)
- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map)
diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs
index dda27271..56da988c 100644
--- a/exercises/06_move_semantics/move_semantics4.rs
+++ b/exercises/06_move_semantics/move_semantics4.rs
@@ -8,11 +8,11 @@ mod tests {
// Don't add, change or remove any line.
#[test]
fn move_semantics4() {
- let mut x = 100;
+ let mut x = Vec::new();
let y = &mut x;
- *y += 100;
let z = &mut x;
- *z += 1000;
- assert_eq!(x, 1200);
+ y.push(42);
+ z.push(13);
+ assert_eq!(x, [42, 13]);
}
}
diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs
index d3e16868..78d8c77e 100644
--- a/exercises/06_move_semantics/move_semantics5.rs
+++ b/exercises/06_move_semantics/move_semantics5.rs
@@ -3,14 +3,6 @@
// TODO: Fix the compiler errors without changing anything except adding or
// removing references (the character `&`).
-fn main() {
- let data = "Rust is great!".to_string();
-
- get_char(&data);
-
- string_uppercase(data);
-}
-
// Shouldn't take ownership
fn get_char(data: &String) -> char {
data.chars().last().unwrap()
@@ -22,3 +14,11 @@ fn string_uppercase(mut data: String) {
println!("{data}");
}
+
+fn main() {
+ let data = "Rust is great!".to_string();
+
+ get_char(&data);
+
+ string_uppercase(data);
+}
diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs
index 27dcaf68..69ae8b8d 100644
--- a/exercises/11_hashmaps/hashmaps2.rs
+++ b/exercises/11_hashmaps/hashmaps2.rs
@@ -6,7 +6,7 @@
// must add fruit to the basket so that there is at least one of each kind and
// more than 11 in total - we have a lot of mouths to feed. You are not allowed
// to insert any more of the fruits that are already in the basket (Apple,
-// Mango, and Lyche).
+// Mango, and Lychee).
use std::collections::HashMap;
diff --git a/exercises/12_options/README.md b/exercises/12_options/README.md
index bdd33749..624572a3 100644
--- a/exercises/12_options/README.md
+++ b/exercises/12_options/README.md
@@ -14,7 +14,7 @@ Option types are very common in Rust code, as they have a number of uses:
## Further Information
-- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions)
+- [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions)
- [Option Module Documentation](https://doc.rust-lang.org/std/option/)
- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html)
- [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html)
diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs
index 58b1e16a..2c1db888 100644
--- a/exercises/13_error_handling/errors3.rs
+++ b/exercises/13_error_handling/errors3.rs
@@ -19,6 +19,7 @@ fn main() {
let mut tokens = 100;
let pretend_user_input = "8";
+ // Don't change this line.
let cost = total_cost(pretend_user_input).unwrap();
if cost > tokens {
diff --git a/exercises/14_generics/README.md b/exercises/14_generics/README.md
index 72cff3f3..0c8c8cb1 100644
--- a/exercises/14_generics/README.md
+++ b/exercises/14_generics/README.md
@@ -7,5 +7,5 @@ The simplest and most common use of generics is for type parameters.
## Further information
-- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html)
+- [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html)
- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)
diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs
index 2f1c1b77..085bb623 100644
--- a/exercises/17_tests/tests3.rs
+++ b/exercises/17_tests/tests3.rs
@@ -9,7 +9,7 @@ impl Rectangle {
if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic.
- panic!("Rectangle width and height can't be negative");
+ panic!("Rectangle width and height must be positive");
}
Rectangle { width, height }
diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs
index 643aa0a8..9f572d53 100644
--- a/exercises/18_iterators/iterators3.rs
+++ b/exercises/18_iterators/iterators3.rs
@@ -1,14 +1,20 @@
#[derive(Debug, PartialEq, Eq)]
enum DivisionError {
+ // Example: 42 / 0
DivideByZero,
+ // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
+ IntegerOverflow,
+ // Example: 5 / 2 = 2.5
NotDivisible,
}
// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
-fn divide(a: i32, b: i32) -> Result {
+fn divide(a: i64, b: i64) -> Result {
if b == 0 {
Err(DivisionError::DivideByZero)
+ } else if (a == i64::MIN && b == -1) {
+ Err(DivisionError::IntegerOverflow)
} else if a % b == 0 {
Ok(a / b)
} else {
@@ -48,6 +54,11 @@ mod tests {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
}
+ #[test]
+ fn test_integer_overflow() {
+ assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
+ }
+
#[test]
fn test_not_divisible() {
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs
index eede5e01..42bc60ec 100644
--- a/exercises/18_iterators/iterators4.rs
+++ b/exercises/18_iterators/iterators4.rs
@@ -1,5 +1,8 @@
fn factorial(num: u64) -> u64 {
- // TODO: Complete this function to return the factorial of `num`.
+ // TODO: Complete this function to return the factorial of `num` which is
+ // defined as `1 * 2 * 3 * … * num`.
+ // https://en.wikipedia.org/wiki/Factorial
+ //
// Do not use:
// - early returns (using the `return` keyword explicitly)
// Try not to use:
diff --git a/exercises/quizzes/quiz2.rs b/exercises/quizzes/quiz2.rs
index ca1339e6..aa2a3383 100644
--- a/exercises/quizzes/quiz2.rs
+++ b/exercises/quizzes/quiz2.rs
@@ -26,7 +26,7 @@ enum Command {
mod my_module {
use super::Command;
- // TODO: Complete the function.
+ // TODO: Complete the function as described above.
pub fn transformer(input: Vec<(String, Command)>) -> Vec {
input.iter().map(|(item, cmd)| {
match cmd {
diff --git a/release-hook.sh b/release-hook.sh
index 052832f2..f021f193 100755
--- a/release-hook.sh
+++ b/release-hook.sh
@@ -3,7 +3,12 @@
# Error out if any command fails
set -e
-cargo run -- dev check
typos
-cargo outdated -w --exit-code 1
+cargo upgrades
+
+# Similar to CI
+cargo clippy -- --deny warnings
+cargo fmt --all --check
+rustfmt --check --edition 2021 solutions/**/*.rs
cargo test --workspace --all-targets
+cargo run -- dev check --require-solutions
diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml
index 715778dd..2ecb2264 100644
--- a/rustlings-macros/info.toml
+++ b/rustlings-macros/info.toml
@@ -309,7 +309,7 @@ In Rust, there are two ways to define a Vector.
inside the square brackets. This way is simpler when you exactly know
the initial values.
-Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
+Check this chapter: https://doc.rust-lang.org/book/ch08-01-vectors.html
of the Rust book to learn more."""
[[exercises]]
@@ -351,7 +351,7 @@ We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's
being "moved" into `vec1`, meaning we can't access `vec0` anymore.
You could make another, separate version of the data that's in `vec0` and
-pass it to `fill_vec` instead."""
+pass it to `fill_vec` instead. This is called cloning in Rust."""
[[exercises]]
name = "move_semantics3"
@@ -378,7 +378,7 @@ dir = "06_move_semantics"
test = false
hint = """
To find the answer, you can consult the book section "References and Borrowing":
-https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html
+https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
The first problem is that `get_char` is taking ownership of the string. So
`data` is moved and can't be used for `string_uppercase`. `data` is moved to
@@ -416,7 +416,7 @@ to its fields.
There are however some shortcuts that can be taken when instantiating structs.
Have a look in The Book to find out more:
-https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
+https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
[[exercises]]
name = "structs3"
@@ -487,7 +487,7 @@ to add one character to the `if` statement, though, that will coerce the
Side note: If you're interested in learning about how this kind of reference
conversion works, you can jump ahead in the book and read this part in the
smart pointers chapter:
-https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
+https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
[[exercises]]
name = "strings3"
@@ -498,7 +498,10 @@ some of them:
https://doc.rust-lang.org/std/string/struct.String.html#method.trim
For the `compose_me` method: You can either use the `format!` macro, or convert
-the string slice into an owned string, which you can then freely extend."""
+the string slice into an owned string, which you can then freely extend.
+
+For the `replace_me` method, you can check out the `replace` method:
+https://doc.rust-lang.org/std/string/struct.String.html#method.replace"""
[[exercises]]
name = "strings4"
@@ -561,7 +564,7 @@ hint = """
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
Learn more in The Book:
-https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
+https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
[[exercises]]
name = "hashmaps3"
@@ -572,7 +575,7 @@ Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of
exist in the table yet.
Learn more in The Book:
-https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
+https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
Hint 2: If there is already an entry for a given key, the value returned by
`entry()` can be updated based on the existing value.
@@ -585,7 +588,7 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-
[[exercises]]
name = "quiz2"
dir = "quizzes"
-hint = "No hints this time ;)"
+hint = "The `+` operator can concatenate a `String` with a `&str`."
# OPTIONS
@@ -739,7 +742,7 @@ name = "generics2"
dir = "14_generics"
hint = """
Related section in The Book:
-https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"""
+https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions"""
# TRAITS
@@ -748,7 +751,9 @@ name = "traits1"
dir = "15_traits"
hint = """
More about traits in The Book:
-https://doc.rust-lang.org/book/ch10-02-traits.html"""
+https://doc.rust-lang.org/book/ch10-02-traits.html
+
+The `+` operator can concatenate a `String` with a `&str`."""
[[exercises]]
name = "traits2"
@@ -869,7 +874,7 @@ We expect the method `Rectangle::new` to panic for negative values.
To handle that, you need to add a special attribute to the test function.
You can refer to the docs:
-https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
+https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
# STANDARD LIBRARY TYPES
@@ -888,9 +893,9 @@ hint = """
`capitalize_first`:
The variable `first` is a `char`. It needs to be capitalized and added to the
-remaining characters in `c` in order to return the correct `String`.
+remaining characters in `chars` in order to return the correct `String`.
-The remaining characters in `c` can be viewed as a string slice using the
+The remaining characters in `chars` can be viewed as a string slice using the
`as_str` method.
The documentation for `char` contains many useful methods.
@@ -1005,7 +1010,7 @@ thread-local copy of the numbers.
This is a simple exercise if you understand the underlying concepts, but if this
is too much of a struggle, consider reading through all of Chapter 16 in The
Book:
-https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
+https://doc.rust-lang.org/book/ch16-00-concurrency.html"""
[[exercises]]
name = "cow1"
@@ -1143,7 +1148,11 @@ test = false
strict_clippy = true
hint = """
`for` loops over `Option` values are more clearly expressed as an `if-let`
-statement."""
+statement.
+
+Not required to solve this exercise, but if you are interested in when iterating
+over `Option` can be useful, read the following section in the documentation:
+https://doc.rust-lang.org/std/option/#iterating-over-option"""
[[exercises]]
name = "clippy3"
diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs
index b7919ac0..64fdd9db 100644
--- a/solutions/06_move_semantics/move_semantics4.rs
+++ b/solutions/06_move_semantics/move_semantics4.rs
@@ -7,15 +7,15 @@ mod tests {
// TODO: Fix the compiler errors only by reordering the lines in the test.
// Don't add, change or remove any line.
#[test]
- fn move_semantics5() {
- let mut x = 100;
+ fn move_semantics4() {
+ let mut x = Vec::new();
let y = &mut x;
// `y` used here.
- *y += 100;
+ y.push(42);
// The mutable reference `y` is not used anymore,
// therefore a new reference can be created.
let z = &mut x;
- *z += 1000;
- assert_eq!(x, 1200);
+ z.push(13);
+ assert_eq!(x, [42, 13]);
}
}
diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs
index 678ec97b..1410e913 100644
--- a/solutions/06_move_semantics/move_semantics5.rs
+++ b/solutions/06_move_semantics/move_semantics5.rs
@@ -1,13 +1,5 @@
#![allow(clippy::ptr_arg)]
-fn main() {
- let data = "Rust is great!".to_string();
-
- get_char(&data);
-
- string_uppercase(data);
-}
-
// Borrows instead of taking ownership.
// It is recommended to use `&str` instead of `&String` here. But this is
// enough for now because we didn't handle strings yet.
@@ -21,3 +13,11 @@ fn string_uppercase(mut data: String) {
println!("{data}");
}
+
+fn main() {
+ let data = "Rust is great!".to_string();
+
+ get_char(&data);
+
+ string_uppercase(data);
+}
diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs
index a5e6ef9b..75e6ec2c 100644
--- a/solutions/11_hashmaps/hashmaps2.rs
+++ b/solutions/11_hashmaps/hashmaps2.rs
@@ -5,7 +5,8 @@
// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You
// must add fruit to the basket so that there is at least one of each kind and
// more than 11 in total - we have a lot of mouths to feed. You are not allowed
-// to insert any more of these fruits!
+// to insert any more of the fruits that are already in the basket (Apple,
+// Mango, and Lychee).
use std::collections::HashMap;
diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs
index 503f9bc0..487fdc66 100644
--- a/solutions/17_tests/tests3.rs
+++ b/solutions/17_tests/tests3.rs
@@ -9,7 +9,7 @@ impl Rectangle {
if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic.
- panic!("Rectangle width and height can't be negative");
+ panic!("Rectangle width and height must be positive");
}
Rectangle { width, height }
diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs
index d66d1ef3..11aa1ec8 100644
--- a/solutions/18_iterators/iterators3.rs
+++ b/solutions/18_iterators/iterators3.rs
@@ -1,6 +1,10 @@
#[derive(Debug, PartialEq, Eq)]
enum DivisionError {
+ // Example: 42 / 0
DivideByZero,
+ // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
+ IntegerOverflow,
+ // Example: 5 / 2 = 2.5
NotDivisible,
}
@@ -9,6 +13,10 @@ fn divide(a: i64, b: i64) -> Result {
return Err(DivisionError::DivideByZero);
}
+ if a == i64::MIN && b == -1 {
+ return Err(DivisionError::IntegerOverflow);
+ }
+
if a % b != 0 {
return Err(DivisionError::NotDivisible);
}
@@ -51,6 +59,11 @@ mod tests {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
}
+ #[test]
+ fn test_integer_overflow() {
+ assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
+ }
+
#[test]
fn test_not_divisible() {
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs
index 2f55f065..58cbe4e2 100644
--- a/solutions/quizzes/quiz2.rs
+++ b/solutions/quizzes/quiz2.rs
@@ -6,7 +6,7 @@
// - Append "bar" to the string a specified amount of times
//
// The exact form of this will be:
-// - The input is going to be a vector of a 2-length tuple,
+// - The input is going to be a vector of 2-length tuples,
// the first element is the string, the second one is the command.
// - The output element is going to be a vector of strings.
diff --git a/src/main.rs b/src/main.rs
index a9f7b4a1..3c96d1a9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
use app_state::StateFileStatus;
use clap::{Parser, Subcommand};
use std::{
- io::{self, BufRead, StdoutLock, Write},
+ io::{self, BufRead, IsTerminal, StdoutLock, Write},
path::Path,
process::exit,
};
@@ -148,6 +148,10 @@ fn main() -> Result<()> {
match args.command {
None => {
+ if !io::stdout().is_terminal() {
+ bail!("Unsupported or missing terminal/TTY");
+ }
+
let notify_exercise_names = if args.manual_run {
None
} else {