rsync code

This commit is contained in:
jiangfangping 2024-07-25 09:27:36 +08:00
commit e77ba8dcf2
26 changed files with 174 additions and 99 deletions

View File

@ -23,7 +23,9 @@ jobs:
with: with:
globs: "exercises/**/*.md" globs: "exercises/**/*.md"
- name: Run cargo fmt - 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: test:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
@ -33,7 +35,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2 - uses: swatinem/rust-cache@v2
- name: Run cargo test - name: Run cargo test
run: cargo test run: cargo test --workspace
dev-check: dev-check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -1,3 +1,22 @@
<a name="6.1.0"></a>
## 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.
<a name="6.0.1"></a> <a name="6.0.1"></a>
## 6.0.1 (2024-07-04) ## 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 📎 Clippy lints are now shown on all exercises, not only the Clippy exercises 📎
Make Clippy your friend from early on 🥰 Make Clippy your friend from early on 🥰
### Third party exercises ### Third-party exercises
Rustlings now supports third-party exercises! Rustlings now supports third-party exercises!

60
Cargo.lock generated
View File

@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.8" version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -161,9 +161,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.8" version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -354,15 +354,6 @@ version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@ -525,7 +516,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall 0.5.2", "redox_syscall 0.5.3",
"smallvec", "smallvec",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
@ -594,7 +585,7 @@ dependencies = [
"cassowary", "cassowary",
"compact_str", "compact_str",
"crossterm", "crossterm",
"itertools 0.13.0", "itertools",
"lru", "lru",
"paste", "paste",
"stability", "stability",
@ -616,9 +607,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.2" version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
] ]
@ -654,7 +645,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]] [[package]]
name = "rustlings" name = "rustlings"
version = "6.0.1" version = "6.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_cmd", "assert_cmd",
@ -673,7 +664,7 @@ dependencies = [
[[package]] [[package]]
name = "rustlings-macros" name = "rustlings-macros"
version = "6.0.1" version = "6.1.0"
dependencies = [ dependencies = [
"quote", "quote",
"serde", "serde",
@ -709,18 +700,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -785,9 +776,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "stability" name = "stability"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
dependencies = [ dependencies = [
"quote", "quote",
"syn", "syn",
@ -829,9 +820,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.68" version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -855,9 +846,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.14" version = "0.22.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -880,11 +871,12 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]] [[package]]
name = "unicode-truncate" name = "unicode-truncate"
version = "1.0.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
dependencies = [ dependencies = [
"itertools 0.12.1", "itertools",
"unicode-segmentation",
"unicode-width", "unicode-width",
] ]
@ -1103,9 +1095,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.13" version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View File

@ -8,7 +8,7 @@ exclude = [
] ]
[workspace.package] [workspace.package]
version = "6.0.1" version = "6.1.0"
authors = [ authors = [
"Liv <mokou@fastmail.com>", "Liv <mokou@fastmail.com>",
"Mo Bitar <mo8it@proton.me>", "Mo Bitar <mo8it@proton.me>",
@ -20,8 +20,8 @@ license = "MIT"
edition = "2021" edition = "2021"
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] } toml_edit = { version = "0.22.16", default-features = false, features = ["parse", "serde"] }
[package] [package]
name = "rustlings" name = "rustlings"
@ -47,13 +47,13 @@ include = [
[dependencies] [dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"
clap = { version = "4.5.8", features = ["derive"] } clap = { version = "4.5.9", features = ["derive"] }
crossterm = "0.27.0" crossterm = "0.27.0"
hashbrown = "0.14.5" hashbrown = "0.14.5"
notify-debouncer-mini = { version = "0.4.1", default-features = false } notify-debouncer-mini = { version = "0.4.1", default-features = false }
os_pipe = "1.2.0" os_pipe = "1.2.0"
ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } 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_json = "1.0.120"
serde.workspace = true serde.workspace = true
toml_edit.workspace = true toml_edit.workspace = true

View File

@ -53,6 +53,21 @@ After installing Rustlings, run the following command to initialize the `rustlin
rustlings init rustlings init
``` ```
<details>
<summary><strong>If the command <code>rustlings</code> can't be found…</strong> (<em>click to expand</em>)</summary>
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
</details>
Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises: Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises:
```bash ```bash

View File

@ -5,5 +5,5 @@ compiler. In this section, we'll go through the most important ones.
## Further information ## Further information
- [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html) - [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html)
- [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html) - [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html)

View File

@ -12,6 +12,6 @@ the other useful data structure, hash maps, later.
## Further information ## 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) - [`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) - [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map)

View File

@ -8,11 +8,11 @@ mod tests {
// Don't add, change or remove any line. // Don't add, change or remove any line.
#[test] #[test]
fn move_semantics4() { fn move_semantics4() {
let mut x = 100; let mut x = Vec::new();
let y = &mut x; let y = &mut x;
*y += 100;
let z = &mut x; let z = &mut x;
*z += 1000; y.push(42);
assert_eq!(x, 1200); z.push(13);
assert_eq!(x, [42, 13]);
} }
} }

View File

@ -3,14 +3,6 @@
// TODO: Fix the compiler errors without changing anything except adding or // TODO: Fix the compiler errors without changing anything except adding or
// removing references (the character `&`). // removing references (the character `&`).
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}
// Shouldn't take ownership // Shouldn't take ownership
fn get_char(data: &String) -> char { fn get_char(data: &String) -> char {
data.chars().last().unwrap() data.chars().last().unwrap()
@ -22,3 +14,11 @@ fn string_uppercase(mut data: String) {
println!("{data}"); println!("{data}");
} }
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}

View File

@ -6,7 +6,7 @@
// must add fruit to the basket so that there is at least one of each kind and // 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 // 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, // to insert any more of the fruits that are already in the basket (Apple,
// Mango, and Lyche). // Mango, and Lychee).
use std::collections::HashMap; use std::collections::HashMap;

View File

@ -14,7 +14,7 @@ Option types are very common in Rust code, as they have a number of uses:
## Further Information ## 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 Module Documentation](https://doc.rust-lang.org/std/option/)
- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html) - [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) - [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html)

View File

@ -19,6 +19,7 @@ fn main() {
let mut tokens = 100; let mut tokens = 100;
let pretend_user_input = "8"; let pretend_user_input = "8";
// Don't change this line.
let cost = total_cost(pretend_user_input).unwrap(); let cost = total_cost(pretend_user_input).unwrap();
if cost > tokens { if cost > tokens {

View File

@ -7,5 +7,5 @@ The simplest and most common use of generics is for type parameters.
## Further information ## 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) - [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)

View File

@ -9,7 +9,7 @@ impl Rectangle {
if width <= 0 || height <= 0 { if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn // Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic. // 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 } Rectangle { width, height }

View File

@ -1,14 +1,20 @@
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum DivisionError { enum DivisionError {
// Example: 42 / 0
DivideByZero, DivideByZero,
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
IntegerOverflow,
// Example: 5 / 2 = 2.5
NotDivisible, NotDivisible,
} }
// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error. // Otherwise, return a suitable error.
fn divide(a: i32, b: i32) -> Result<i32, DivisionError> { fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
if b == 0 { if b == 0 {
Err(DivisionError::DivideByZero) Err(DivisionError::DivideByZero)
} else if (a == i64::MIN && b == -1) {
Err(DivisionError::IntegerOverflow)
} else if a % b == 0 { } else if a % b == 0 {
Ok(a / b) Ok(a / b)
} else { } else {
@ -48,6 +54,11 @@ mod tests {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
} }
#[test]
fn test_integer_overflow() {
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
}
#[test] #[test]
fn test_not_divisible() { fn test_not_divisible() {
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));

View File

@ -1,5 +1,8 @@
fn factorial(num: u64) -> u64 { 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: // Do not use:
// - early returns (using the `return` keyword explicitly) // - early returns (using the `return` keyword explicitly)
// Try not to use: // Try not to use:

View File

@ -26,7 +26,7 @@ enum Command {
mod my_module { mod my_module {
use super::Command; use super::Command;
// TODO: Complete the function. // TODO: Complete the function as described above.
pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> { pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
input.iter().map(|(item, cmd)| { input.iter().map(|(item, cmd)| {
match cmd { match cmd {

View File

@ -3,7 +3,12 @@
# Error out if any command fails # Error out if any command fails
set -e set -e
cargo run -- dev check
typos 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 test --workspace --all-targets
cargo run -- dev check --require-solutions

View File

@ -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 inside the square brackets. This way is simpler when you exactly know
the initial values. 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.""" of the Rust book to learn more."""
[[exercises]] [[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. 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 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]] [[exercises]]
name = "move_semantics3" name = "move_semantics3"
@ -378,7 +378,7 @@ dir = "06_move_semantics"
test = false test = false
hint = """ hint = """
To find the answer, you can consult the book section "References and Borrowing": 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 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 `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. There are however some shortcuts that can be taken when instantiating structs.
Have a look in The Book to find out more: 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]] [[exercises]]
name = "structs3" 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 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 conversion works, you can jump ahead in the book and read this part in the
smart pointers chapter: 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]] [[exercises]]
name = "strings3" name = "strings3"
@ -498,7 +498,10 @@ some of them:
https://doc.rust-lang.org/std/string/struct.String.html#method.trim 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 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]] [[exercises]]
name = "strings4" name = "strings4"
@ -561,7 +564,7 @@ hint = """
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
Learn more in The Book: 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]] [[exercises]]
name = "hashmaps3" 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. exist in the table yet.
Learn more in The Book: 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 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. `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]] [[exercises]]
name = "quiz2" name = "quiz2"
dir = "quizzes" dir = "quizzes"
hint = "No hints this time ;)" hint = "The `+` operator can concatenate a `String` with a `&str`."
# OPTIONS # OPTIONS
@ -739,7 +742,7 @@ name = "generics2"
dir = "14_generics" dir = "14_generics"
hint = """ hint = """
Related section in The Book: 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 # TRAITS
@ -748,7 +751,9 @@ name = "traits1"
dir = "15_traits" dir = "15_traits"
hint = """ hint = """
More about traits in The Book: 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]] [[exercises]]
name = "traits2" 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. To handle that, you need to add a special attribute to the test function.
You can refer to the docs: 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 # STANDARD LIBRARY TYPES
@ -888,9 +893,9 @@ hint = """
`capitalize_first`: `capitalize_first`:
The variable `first` is a `char`. It needs to be capitalized and added to the 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. `as_str` method.
The documentation for `char` contains many useful methods. 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 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 is too much of a struggle, consider reading through all of Chapter 16 in The
Book: Book:
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html""" https://doc.rust-lang.org/book/ch16-00-concurrency.html"""
[[exercises]] [[exercises]]
name = "cow1" name = "cow1"
@ -1143,7 +1148,11 @@ test = false
strict_clippy = true strict_clippy = true
hint = """ hint = """
`for` loops over `Option` values are more clearly expressed as an `if-let` `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]] [[exercises]]
name = "clippy3" name = "clippy3"

View File

@ -7,15 +7,15 @@ mod tests {
// TODO: Fix the compiler errors only by reordering the lines in the test. // TODO: Fix the compiler errors only by reordering the lines in the test.
// Don't add, change or remove any line. // Don't add, change or remove any line.
#[test] #[test]
fn move_semantics5() { fn move_semantics4() {
let mut x = 100; let mut x = Vec::new();
let y = &mut x; let y = &mut x;
// `y` used here. // `y` used here.
*y += 100; y.push(42);
// The mutable reference `y` is not used anymore, // The mutable reference `y` is not used anymore,
// therefore a new reference can be created. // therefore a new reference can be created.
let z = &mut x; let z = &mut x;
*z += 1000; z.push(13);
assert_eq!(x, 1200); assert_eq!(x, [42, 13]);
} }
} }

View File

@ -1,13 +1,5 @@
#![allow(clippy::ptr_arg)] #![allow(clippy::ptr_arg)]
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}
// Borrows instead of taking ownership. // Borrows instead of taking ownership.
// It is recommended to use `&str` instead of `&String` here. But this is // It is recommended to use `&str` instead of `&String` here. But this is
// enough for now because we didn't handle strings yet. // enough for now because we didn't handle strings yet.
@ -21,3 +13,11 @@ fn string_uppercase(mut data: String) {
println!("{data}"); println!("{data}");
} }
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}

View File

@ -5,7 +5,8 @@
// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You // 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 // 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 // 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; use std::collections::HashMap;

View File

@ -9,7 +9,7 @@ impl Rectangle {
if width <= 0 || height <= 0 { if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn // Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic. // 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 } Rectangle { width, height }

View File

@ -1,6 +1,10 @@
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum DivisionError { enum DivisionError {
// Example: 42 / 0
DivideByZero, DivideByZero,
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
IntegerOverflow,
// Example: 5 / 2 = 2.5
NotDivisible, NotDivisible,
} }
@ -9,6 +13,10 @@ fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
return Err(DivisionError::DivideByZero); return Err(DivisionError::DivideByZero);
} }
if a == i64::MIN && b == -1 {
return Err(DivisionError::IntegerOverflow);
}
if a % b != 0 { if a % b != 0 {
return Err(DivisionError::NotDivisible); return Err(DivisionError::NotDivisible);
} }
@ -51,6 +59,11 @@ mod tests {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
} }
#[test]
fn test_integer_overflow() {
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
}
#[test] #[test]
fn test_not_divisible() { fn test_not_divisible() {
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));

View File

@ -6,7 +6,7 @@
// - Append "bar" to the string a specified amount of times // - Append "bar" to the string a specified amount of times
// //
// The exact form of this will be: // 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 first element is the string, the second one is the command.
// - The output element is going to be a vector of strings. // - The output element is going to be a vector of strings.

View File

@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
use app_state::StateFileStatus; use app_state::StateFileStatus;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::{ use std::{
io::{self, BufRead, StdoutLock, Write}, io::{self, BufRead, IsTerminal, StdoutLock, Write},
path::Path, path::Path,
process::exit, process::exit,
}; };
@ -148,6 +148,10 @@ fn main() -> Result<()> {
match args.command { match args.command {
None => { None => {
if !io::stdout().is_terminal() {
bail!("Unsupported or missing terminal/TTY");
}
let notify_exercise_names = if args.manual_run { let notify_exercise_names = if args.manual_run {
None None
} else { } else {