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 {