Compare commits

..

No commits in common. "47f8199a996c098cfd64f5afa08284a588d2b874" and "0f71e122357f3f214c1d9db71f1d84c9f92adc5f" have entirely different histories.

35 changed files with 100 additions and 172 deletions

View file

@ -40,4 +40,4 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2 - uses: swatinem/rust-cache@v2
- name: Run rustlings dev check - name: Run rustlings dev check
run: cargo run -- dev check --require-solutions run: cargo run -- dev check

View file

@ -1,10 +1,3 @@
<a name="6.0.1"></a>
## 6.0.1 (2024-07-04)
Small exercise improvements and fixes.
Most importantly, fixed that the exercise `clippy1` was already solved 😅
<a name="6.0.0"></a> <a name="6.0.0"></a>
## 6.0.0 (2024-07-03) ## 6.0.0 (2024-07-03)

62
Cargo.lock generated
View file

@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]] [[package]]
name = "castaway" name = "castaway"
version = "0.2.3" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
dependencies = [ dependencies = [
"rustversion", "rustversion",
] ]
@ -527,7 +527,7 @@ dependencies = [
"libc", "libc",
"redox_syscall 0.5.2", "redox_syscall 0.5.2",
"smallvec", "smallvec",
"windows-targets 0.52.6", "windows-targets 0.52.5",
] ]
[[package]] [[package]]
@ -654,7 +654,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]] [[package]]
name = "rustlings" name = "rustlings"
version = "6.0.1" version = "6.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_cmd", "assert_cmd",
@ -673,7 +673,7 @@ dependencies = [
[[package]] [[package]]
name = "rustlings-macros" name = "rustlings-macros"
version = "6.0.1" version = "6.0.0"
dependencies = [ dependencies = [
"quote", "quote",
"serde", "serde",
@ -977,7 +977,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.5",
] ]
[[package]] [[package]]
@ -997,18 +997,18 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.6", "windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.6", "windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.6", "windows_i686_gnu 0.52.5",
"windows_i686_gnullvm", "windows_i686_gnullvm",
"windows_i686_msvc 0.52.6", "windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.6", "windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.6", "windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.6", "windows_x86_64_msvc 0.52.5",
] ]
[[package]] [[package]]
@ -1019,9 +1019,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@ -1031,9 +1031,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@ -1043,15 +1043,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@ -1061,9 +1061,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@ -1073,9 +1073,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@ -1085,9 +1085,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@ -1097,9 +1097,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]] [[package]]
name = "winnow" name = "winnow"

View file

@ -8,7 +8,7 @@ exclude = [
] ]
[workspace.package] [workspace.package]
version = "6.0.1" version = "6.0.0"
authors = [ authors = [
"Liv <mokou@fastmail.com>", "Liv <mokou@fastmail.com>",
"Mo Bitar <mo8it@proton.me>", "Mo Bitar <mo8it@proton.me>",
@ -53,7 +53,7 @@ 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.0.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

@ -3,7 +3,8 @@
// ready for the next exercise, enter `n` in the terminal. // ready for the next exercise, enter `n` in the terminal.
// //
// The exercise file will be reloaded when you change one of the lines below! // The exercise file will be reloaded when you change one of the lines below!
// Try adding a new `println!` and check the updated output in the terminal. // Try adding a new `println!`.
// Try removing a semicolon and see what happens in the terminal!
fn main() { fn main() {
println!("Hello and"); println!("Hello and");

View file

@ -7,7 +7,7 @@ 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_semantics4() { fn move_semantics5() {
let mut x = 100; let mut x = 100;
let y = &mut x; let y = &mut x;
let z = &mut x; let z = &mut x;

View file

@ -1,5 +1,3 @@
#![allow(clippy::ptr_arg)]
// 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 `&`).
@ -18,7 +16,7 @@ fn get_char(data: String) -> char {
// Should take ownership // Should take ownership
fn string_uppercase(mut data: &String) { fn string_uppercase(mut data: &String) {
data = data.to_uppercase(); data = &data.to_uppercase();
println!("{data}"); println!("{data}");
} }

View file

@ -1,4 +1,3 @@
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum Message { enum Message {
// TODO: Define the different variants used below. // TODO: Define the different variants used below.
@ -6,7 +5,7 @@ enum Message {
impl Message { impl Message {
fn call(&self) { fn call(&self) {
println!("{self:?}"); println!("{:?}", self);
} }
} }

View file

@ -1,7 +1,6 @@
// You can bring module paths into scopes and provide new names for them with // You can bring module paths into scopes and provide new names for them with
// the `use` and `as` keywords. // the `use` and `as` keywords.
#[allow(dead_code)]
mod delicious_snacks { mod delicious_snacks {
// TODO: Add the following two `use` statements after fixing them. // TODO: Add the following two `use` statements after fixing them.
// use self::fruits::PEAR as ???; // use self::fruits::PEAR as ???;

View file

@ -19,8 +19,7 @@ mod tests {
// TODO: Fix this test. How do you get the value contained in the // TODO: Fix this test. How do you get the value contained in the
// Option? // Option?
let icecreams = maybe_icecream(12); let icecreams = maybe_icecream(12);
assert_eq!(icecreams, 5);
assert_eq!(icecreams, 5); // Don't change this line.
} }
#[test] #[test]

View file

@ -5,11 +5,11 @@
// the player typed in the quantity, we get it as a string. They might have // the player typed in the quantity, we get it as a string. They might have
// typed anything, not just numbers! // typed anything, not just numbers!
// //
// Right now, this function isn't handling the error case at all. What we want // Right now, this function isn't handling the error case at all (and isn't
// to do is: If we call the `total_cost` function on a string that is not a // handling the success case properly either). What we want to do is: If we call
// number, that function will return a `ParseIntError`. In that case, we want to // the `total_cost` function on a string that is not a number, that function
// immediately return that error from our function and not try to multiply and // will return a `ParseIntError`. In that case, we want to immediately return
// add. // that error from our function and not try to multiply and add.
// //
// There are at least two ways to implement this that are both correct. But one // There are at least two ways to implement this that are both correct. But one
// is a lot shorter! // is a lot shorter!

View file

@ -1,5 +1,3 @@
#![allow(clippy::comparison_chain)]
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
enum CreationError { enum CreationError {
Negative, Negative,

View file

@ -35,7 +35,7 @@ impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> { fn new(value: i64) -> Result<Self, CreationError> {
match value { match value {
x if x < 0 => Err(CreationError::Negative), x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero), x if x == 0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)), x => Ok(Self(x as u64)),
} }
} }
@ -55,6 +55,7 @@ fn main() {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use std::num::IntErrorKind;
#[test] #[test]
fn test_parse_error() { fn test_parse_error() {

View file

@ -1,5 +1,3 @@
#![allow(dead_code)]
trait Licensed { trait Licensed {
// TODO: Add a default implementation for `licensing_info` so that // TODO: Add a default implementation for `licensing_info` so that
// implementors like the two structs below can share that default behavior // implementors like the two structs below can share that default behavior

View file

@ -54,7 +54,7 @@ mod tests {
#[test] #[test]
fn test_result_with_list() { fn test_result_with_list() {
assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]);
} }
#[test] #[test]

View file

@ -1,4 +1,4 @@
// In this exercise, we are given a `Vec` of `u32` called `numbers` with values // In this exercise, we are given a `Vec` of u32 called `numbers` with values
// ranging from 0 to 99. We would like to use this set of numbers within 8 // ranging from 0 to 99. We would like to use this set of numbers within 8
// different threads simultaneously. Each thread is going to get the sum of // different threads simultaneously. Each thread is going to get the sum of
// every eighth value with an offset. // every eighth value with an offset.
@ -9,11 +9,8 @@
// … // …
// The eighth thread (offset 7), will sum 7, 15, 23, … // The eighth thread (offset 7), will sum 7, 15, 23, …
// //
// Each thread should own a reference-counting pointer to the vector of // Because we are using threads, our values need to be thread-safe. Therefore,
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. // we are using `Arc`.
//
// Don't get distracted by how threads are spawned and joined. We will practice
// that later in the exercises about threads.
// Don't change the lines below. // Don't change the lines below.
#![forbid(unused_imports)] #![forbid(unused_imports)]

View file

@ -8,7 +8,6 @@ use std::rc::Rc;
#[derive(Debug)] #[derive(Debug)]
struct Sun; struct Sun;
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum Planet { enum Planet {
Mercury(Rc<Sun>), Mercury(Rc<Sun>),

View file

@ -4,9 +4,11 @@
// For these exercises, the code will fail to compile when there are Clippy // For these exercises, the code will fail to compile when there are Clippy
// warnings. Check Clippy's suggestions from the output to solve the exercise. // warnings. Check Clippy's suggestions from the output to solve the exercise.
use std::f32::consts::PI;
fn main() { fn main() {
// TODO: Fix the Clippy lint in this line. // Use the more accurate `PI` constant.
let pi = 3.14; let pi = PI;
let radius: f32 = 5.0; let radius: f32 = 5.0;
let area = pi * radius.powi(2); let area = pi * radius.powi(2);

View file

@ -6,8 +6,8 @@
// Mary is buying apples. The price of an apple is calculated as follows: // Mary is buying apples. The price of an apple is calculated as follows:
// - An apple costs 2 rustbucks. // - An apple costs 2 rustbucks.
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck! // - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
// TODO: Write a function that calculates the price of an order of apples given // Write a function that calculates the price of an order of apples given the
// the quantity bought. // quantity bought.
// Put your function here! // Put your function here!
// fn calculate_price_of_apples(???) -> ??? { // fn calculate_price_of_apples(???) -> ??? {

View file

@ -16,14 +16,16 @@ get started, here are some notes about how Rustlings operates:
3. If you're stuck on an exercise, enter `h` to show a hint. 3. If you're stuck on an exercise, enter `h` to show a hint.
4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub!
(https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes,
other learners do too so you can help each other out!""" other learners do too so you can help each other out!
"""
final_message = """We hope you enjoyed learning about the various aspects of Rust! final_message = """We hope you enjoyed learning about the various aspects of Rust!
If you noticed any issues, don't hesitate to report them on Github. If you noticed any issues, don't hesitate to report them on Github.
You can also contribute your own exercises to help the greater community! You can also contribute your own exercises to help the greater community!
Before reporting an issue or contributing, please read our guidelines: Before reporting an issue or contributing, please read our guidelines:
https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md""" https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md
"""
# INTRO # INTRO
@ -31,7 +33,6 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"""
name = "intro1" name = "intro1"
dir = "00_intro" dir = "00_intro"
test = false test = false
skip_check_unsolved = true
hint = """ hint = """
Enter `n` to move on to the next exercise. Enter `n` to move on to the next exercise.
You might need to press ENTER after typing `n`.""" You might need to press ENTER after typing `n`."""
@ -129,7 +130,8 @@ The type of Constants must always be annotated.
Read more about constants and the differences between variables and constants Read more about constants and the differences between variables and constants
under 'Constants' in the book's section 'Variables and Mutability': under 'Constants' in the book's section 'Variables and Mutability':
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants""" https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants
"""
# FUNCTIONS # FUNCTIONS
@ -310,7 +312,8 @@ In Rust, there are two ways to define a Vector.
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/stable/book/ch08-01-vectors.html
of the Rust book to learn more.""" of the Rust book to learn more.
"""
[[exercises]] [[exercises]]
name = "vecs2" name = "vecs2"
@ -324,7 +327,8 @@ In the second function, we map the values of the input and collect them into a v
After you've completed both functions, decide for yourself which approach you After you've completed both functions, decide for yourself which approach you
like better. like better.
What do you think is the more commonly used pattern under Rust developers?""" What do you think is the more commonly used pattern under Rust developers?
"""
# MOVE SEMANTICS # MOVE SEMANTICS
@ -351,7 +355,8 @@ 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.
"""
[[exercises]] [[exercises]]
name = "move_semantics3" name = "move_semantics3"
@ -370,7 +375,8 @@ Carefully reason about the range in which each mutable reference is in
scope. Does it help to update the value of `x` immediately after scope. Does it help to update the value of `x` immediately after
the mutable reference is taken? the mutable reference is taken?
Read more about 'Mutable References' in the book's section 'References and Borrowing': Read more about 'Mutable References' in the book's section 'References and Borrowing':
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.""" https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
"""
[[exercises]] [[exercises]]
name = "move_semantics5" name = "move_semantics5"
@ -511,7 +517,8 @@ Example:
`placeholder("blue");` `placeholder("blue");`
should become should become
`string_slice("blue");` `string_slice("blue");`
because "blue" is `&str`, not `String`.""" because "blue" is `&str`, not `String`.
"""
# MODULES # MODULES
@ -613,7 +620,8 @@ Remember that `Option`s can be nested in if-let and while-let statements.
For example: `if let Some(Some(x)) = y` For example: `if let Some(Some(x)) = y`
Also see `Option::flatten`""" Also see `Option::flatten`
"""
[[exercises]] [[exercises]]
name = "options3" name = "options3"
@ -805,7 +813,8 @@ Here is how to specify a trait bound for an implementation block:
`impl<T: Trait1 + Trait2 + > for Foo<T> { }` `impl<T: Trait1 + Trait2 + > for Foo<T> { }`
You may need this: You may need this:
`use std::fmt::Display;`""" `use std::fmt::Display;`
"""
# LIFETIMES # LIFETIMES

View file

@ -1,5 +1,3 @@
#![allow(clippy::needless_late_init)]
fn main() { fn main() {
// Reading uninitialized variables isn't allowed in Rust! // Reading uninitialized variables isn't allowed in Rust!
// Therefore, we need to assign a value first. // Therefore, we need to assign a value first.
@ -7,7 +5,7 @@ fn main() {
println!("Number {x}"); println!("Number {x}");
// It is possible to declare a variable and initialize it later. // It possible to declare a variable and initialize it later.
// But it can't be used before initialization. // But it can't be used before initialization.
let y: i32; let y: i32;
y = 42; y = 42;

View file

@ -1,5 +1,3 @@
#![allow(clippy::ptr_arg)]
fn main() { fn main() {
let data = "Rust is great!".to_string(); let data = "Rust is great!".to_string();

View file

@ -1,4 +1,3 @@
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum Message { enum Message {
Move { x: i64, y: i64 }, Move { x: i64, y: i64 },
@ -9,7 +8,7 @@ enum Message {
impl Message { impl Message {
fn call(&self) { fn call(&self) {
println!("{self:?}"); println!("{:?}", self);
} }
} }

View file

@ -1,4 +1,3 @@
#[allow(dead_code)]
mod delicious_snacks { mod delicious_snacks {
// Added `pub` and used the expected alias after `as`. // Added `pub` and used the expected alias after `as`.
pub use self::fruits::PEAR as fruit; pub use self::fruits::PEAR as fruit;

View file

@ -28,13 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, Team> {
let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
// Insert the default with zeros if a team doesn't exist yet. // Insert the default with zeros if a team doesn't exist yet.
let team_1 = scores.entry(team_1_name).or_insert_with(Team::default); let mut team_1 = scores.entry(team_1_name).or_insert_with(|| Team::default());
// Update the values. // Update the values.
team_1.goals_scored += team_1_score; team_1.goals_scored += team_1_score;
team_1.goals_conceded += team_2_score; team_1.goals_conceded += team_2_score;
// Similarely for the second team. // Similarely for the second team.
let team_2 = scores.entry(team_2_name).or_insert_with(Team::default); let mut team_2 = scores.entry(team_2_name).or_insert_with(|| Team::default());
team_2.goals_scored += team_2_score; team_2.goals_scored += team_2_score;
team_2.goals_conceded += team_1_score; team_2.goals_conceded += team_1_score;
} }

View file

@ -22,7 +22,6 @@ mod tests {
fn raw_value() { fn raw_value() {
// Using `unwrap` is fine in a test. // Using `unwrap` is fine in a test.
let icecreams = maybe_icecream(12).unwrap(); let icecreams = maybe_icecream(12).unwrap();
assert_eq!(icecreams, 5); assert_eq!(icecreams, 5);
} }

View file

@ -5,18 +5,17 @@
// the player typed in the quantity, we get it as a string. They might have // the player typed in the quantity, we get it as a string. They might have
// typed anything, not just numbers! // typed anything, not just numbers!
// //
// Right now, this function isn't handling the error case at all. What we want // Right now, this function isn't handling the error case at all (and isn't
// to do is: If we call the `total_cost` function on a string that is not a // handling the success case properly either). What we want to do is: If we call
// number, that function will return a `ParseIntError`. In that case, we want to // the `total_cost` function on a string that is not a number, that function
// immediately return that error from our function and not try to multiply and // will return a `ParseIntError`. In that case, we want to immediately return
// add. // that error from our function and not try to multiply and add.
// //
// There are at least two ways to implement this that are both correct. But one // There are at least two ways to implement this that are both correct. But one
// is a lot shorter! // is a lot shorter!
use std::num::ParseIntError; use std::num::ParseIntError;
#[allow(unused_variables)]
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> { fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1; let processing_fee = 1;
let cost_per_item = 5; let cost_per_item = 5;

View file

@ -1,5 +1,3 @@
#![allow(clippy::comparison_chain)]
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
enum CreationError { enum CreationError {
Negative, Negative,

View file

@ -36,7 +36,7 @@ impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> { fn new(value: i64) -> Result<Self, CreationError> {
match value { match value {
x if x < 0 => Err(CreationError::Negative), x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero), x if x == 0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)), x => Ok(Self(x as u64)),
} }
} }
@ -57,6 +57,7 @@ fn main() {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use std::num::IntErrorKind;
#[test] #[test]
fn test_parse_error() { fn test_parse_error() {

View file

@ -1,5 +1,3 @@
#![allow(dead_code)]
trait Licensed { trait Licensed {
fn licensing_info(&self) -> String { fn licensing_info(&self) -> String {
"Default license".to_string() "Default license".to_string()

View file

@ -1,4 +1,4 @@
// In this exercise, we are given a `Vec` of `u32` called `numbers` with values // In this exercise, we are given a `Vec` of u32 called `numbers` with values
// ranging from 0 to 99. We would like to use this set of numbers within 8 // ranging from 0 to 99. We would like to use this set of numbers within 8
// different threads simultaneously. Each thread is going to get the sum of // different threads simultaneously. Each thread is going to get the sum of
// every eighth value with an offset. // every eighth value with an offset.
@ -9,11 +9,8 @@
// … // …
// The eighth thread (offset 7), will sum 7, 15, 23, … // The eighth thread (offset 7), will sum 7, 15, 23, …
// //
// Each thread should own a reference-counting pointer to the vector of // Because we are using threads, our values need to be thread-safe. Therefore,
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. // we are using `Arc`.
//
// Don't get distracted by how threads are spawned and joined. We will practice
// that later in the exercises about threads.
// Don't change the lines below. // Don't change the lines below.
#![forbid(unused_imports)] #![forbid(unused_imports)]

View file

@ -8,7 +8,6 @@ use std::rc::Rc;
#[derive(Debug)] #[derive(Debug)]
struct Sun; struct Sun;
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
enum Planet { enum Planet {
Mercury(Rc<Sun>), Mercury(Rc<Sun>),

View file

@ -4,7 +4,6 @@
// type itself. You can read more about it in the documentation: // type itself. You can read more about it in the documentation:
// https://doc.rust-lang.org/std/convert/trait.TryFrom.html // https://doc.rust-lang.org/std/convert/trait.TryFrom.html
#![allow(clippy::useless_vec)]
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]

View file

@ -92,10 +92,6 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<hashbrown::HashSet<
bail!("The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors"); bail!("The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors");
} }
if !file_buf.contains("// TODO") {
bail!("Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user.");
}
if !exercise_info.test && file_buf.contains("#[test]") { if !exercise_info.test && file_buf.contains("#[test]") {
bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file"); bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file");
} }
@ -162,46 +158,7 @@ fn check_unexpected_files(
Ok(()) Ok(())
} }
fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<()> { fn check_exercises(info_file: &InfoFile) -> Result<()> {
let error_occurred = AtomicBool::new(false);
println!(
"Running all exercises to check that they aren't already solved. This may take a while…\n",
);
thread::scope(|s| {
for exercise_info in &info_file.exercises {
if exercise_info.skip_check_unsolved {
continue;
}
s.spawn(|| {
let error = |e| {
let mut stderr = io::stderr().lock();
stderr.write_all(e).unwrap();
stderr.write_all(b"\nProblem with the exercise ").unwrap();
stderr.write_all(exercise_info.name.as_bytes()).unwrap();
stderr.write_all(SEPARATOR).unwrap();
error_occurred.store(true, atomic::Ordering::Relaxed);
};
let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
match exercise_info.run_exercise(&mut output, target_dir) {
Ok(true) => error(b"Already solved!"),
Ok(false) => (),
Err(e) => error(e.to_string().as_bytes()),
}
});
}
});
if error_occurred.load(atomic::Ordering::Relaxed) {
bail!(CHECK_EXERCISES_UNSOLVED_ERR);
}
Ok(())
}
fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> Result<()> {
match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) { match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) {
Ordering::Less => bail!("`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\nPlease migrate to the latest format version"), Ordering::Less => bail!("`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\nPlease migrate to the latest format version"),
Ordering::Greater => bail!("`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program"), Ordering::Greater => bail!("`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program"),
@ -211,14 +168,15 @@ fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> Result<()> {
let info_file_paths = check_info_file_exercises(info_file)?; let info_file_paths = check_info_file_exercises(info_file)?;
check_unexpected_files("exercises", &info_file_paths)?; check_unexpected_files("exercises", &info_file_paths)?;
check_exercises_unsolved(info_file, target_dir) Ok(())
} }
fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> Result<()> { fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> {
let target_dir = parse_target_dir()?;
let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len()));
let error_occurred = AtomicBool::new(false); let error_occurred = AtomicBool::new(false);
println!("Running all solutions. This may take a while\n"); println!("Running all solutions. This may take a while...\n");
thread::scope(|s| { thread::scope(|s| {
for exercise_info in &info_file.exercises { for exercise_info in &info_file.exercises {
s.spawn(|| { s.spawn(|| {
@ -244,7 +202,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &P
} }
let mut output = Vec::with_capacity(OUTPUT_CAPACITY); let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
match exercise_info.run_solution(&mut output, target_dir) { match exercise_info.run_solution(&mut output, &target_dir) {
Ok(true) => { Ok(true) => {
paths.lock().unwrap().insert(PathBuf::from(path)); paths.lock().unwrap().insert(PathBuf::from(path));
} }
@ -280,9 +238,8 @@ pub fn check(require_solutions: bool) -> Result<()> {
check_cargo_toml(&info_file.exercises, &current_cargo_toml, b"")?; check_cargo_toml(&info_file.exercises, &current_cargo_toml, b"")?;
} }
let target_dir = parse_target_dir()?; check_exercises(&info_file)?;
check_exercises(&info_file, &target_dir)?; check_solutions(require_solutions, &info_file)?;
check_solutions(require_solutions, &info_file, &target_dir)?;
println!("\nEverything looks fine!"); println!("\nEverything looks fine!");
@ -291,6 +248,3 @@ pub fn check(require_solutions: bool) -> Result<()> {
const SEPARATOR: &[u8] = const SEPARATOR: &[u8] =
b"\n========================================================================================\n"; b"\n========================================================================================\n";
const CHECK_EXERCISES_UNSOLVED_ERR: &str = "At least one exercise is already solved or failed to run. See the output above.
If this is an intro exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file.";

View file

@ -11,17 +11,14 @@ pub struct ExerciseInfo {
pub name: String, pub name: String,
/// Exercise's directory name inside the `exercises/` directory. /// Exercise's directory name inside the `exercises/` directory.
pub dir: Option<String>, pub dir: Option<String>,
/// Run `cargo test` on the exercise.
#[serde(default = "default_true")] #[serde(default = "default_true")]
/// Run `cargo test` on the exercise.
pub test: bool, pub test: bool,
/// Deny all Clippy warnings. /// Deny all Clippy warnings.
#[serde(default)] #[serde(default)]
pub strict_clippy: bool, pub strict_clippy: bool,
/// The exercise's hint to be shown to the user on request. /// The exercise's hint to be shown to the user on request.
pub hint: String, pub hint: String,
/// The exercise is already solved. Ignore it when checking that all exercises are unsolved.
#[serde(default)]
pub skip_check_unsolved: bool,
} }
#[inline(always)] #[inline(always)]
const fn default_true() -> bool { const fn default_true() -> bool {