Compare commits

...

22 commits

Author SHA1 Message Date
Mo 47f8199a99
Merge pull request #2017 from Nahor/main
Fix misleading test name
2024-07-04 21:14:02 +02:00
mo8it 4bf0ddc0e1 Check exercises unsolved 2024-07-04 21:12:57 +02:00
Nahor 4cd0ccce83 Fix misleading test name 2024-07-04 11:58:09 -07:00
mo8it a3657188b6 Check for missing TODO comments 2024-07-04 20:28:46 +02:00
mo8it b8fcd11286 chore: Release 2024-07-04 20:02:43 +02:00
mo8it 4810555038 Update CHANGELOG 2024-07-04 20:02:30 +02:00
mo8it 84b291852c Update deps 2024-07-04 19:48:09 +02:00
mo8it 74831dd88f Add TODO 2024-07-04 19:46:43 +02:00
mo8it 0b220f9fff Fix clippy1 2024-07-04 19:46:43 +02:00
Mo d3cdeed871
Merge pull request #2015 from ramenhost/fix-move_semantics5
move_semantics5: change to fix misleading compiler error
2024-07-04 16:25:57 +02:00
Ramkumar 0524632199 fix move_semantics5 to change misleading compiler error 2024-07-04 18:48:09 +05:30
mo8it 30d5b7db92 Require solutions 2024-07-04 13:41:03 +02:00
mo8it 2f60f4d9ea Remove newline at the end of multiline strings 2024-07-04 13:38:57 +02:00
mo8it b017b87866 Fix typo 2024-07-04 13:38:41 +02:00
mo8it b87aa98634 Fix warnings 2024-07-04 13:38:35 +02:00
mo8it a4c07ca948 Improve the comment in intro1 2024-07-04 13:10:18 +02:00
mo8it b8826dd3b3 Remove comment about removing a semicolon 2024-07-04 13:08:59 +02:00
mo8it d54c050985 Improve a comment in errors2 2024-07-04 13:03:05 +02:00
mo8it 248dd4415e Add comment to options1 2024-07-04 13:00:04 +02:00
mo8it dec6772b05 Improve the comment of arc1 2024-07-04 12:58:04 +02:00
Mo b4f4c79ac4
Merge pull request #2012 from cbochs/main
fix: typo s/unwarp/unwrap/
2024-07-04 09:00:31 +02:00
Calvin Bochulak c1d5252b87
fix: typo s/unwarp/unwrap/ 2024-07-03 23:20:59 -06:00
35 changed files with 172 additions and 100 deletions

View file

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

View file

@ -1,3 +1,10 @@
<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>
## 6.0.0 (2024-07-03)

62
Cargo.lock generated
View file

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

View file

@ -8,7 +8,7 @@ exclude = [
]
[workspace.package]
version = "6.0.0"
version = "6.0.1"
authors = [
"Liv <mokou@fastmail.com>",
"Mo Bitar <mo8it@proton.me>",
@ -53,7 +53,7 @@ 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.0" }
rustlings-macros = { path = "rustlings-macros", version = "=6.0.1" }
serde_json = "1.0.120"
serde.workspace = true
toml_edit.workspace = true

View file

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

View file

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

View file

@ -1,3 +1,5 @@
#![allow(clippy::ptr_arg)]
// TODO: Fix the compiler errors without changing anything except adding or
// removing references (the character `&`).
@ -16,7 +18,7 @@ fn get_char(data: String) -> char {
// Should take ownership
fn string_uppercase(mut data: &String) {
data = &data.to_uppercase();
data = data.to_uppercase();
println!("{data}");
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -54,7 +54,7 @@ mod tests {
#[test]
fn test_result_with_list() {
assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]);
assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]);
}
#[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
// different threads simultaneously. Each thread is going to get the sum of
// every eighth value with an offset.
@ -9,8 +9,11 @@
// …
// The eighth thread (offset 7), will sum 7, 15, 23, …
//
// Because we are using threads, our values need to be thread-safe. Therefore,
// we are using `Arc`.
// Each thread should own a reference-counting pointer to the vector of
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `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.
#![forbid(unused_imports)]

View file

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

View file

@ -4,11 +4,9 @@
// 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.
use std::f32::consts::PI;
fn main() {
// Use the more accurate `PI` constant.
let pi = PI;
// TODO: Fix the Clippy lint in this line.
let pi = 3.14;
let radius: f32 = 5.0;
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:
// - An apple costs 2 rustbucks.
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
// Write a function that calculates the price of an order of apples given the
// quantity bought.
// TODO: Write a function that calculates the price of an order of apples given
// the quantity bought.
// Put your function here!
// fn calculate_price_of_apples(???) -> ??? {

View file

@ -16,16 +16,14 @@ get started, here are some notes about how Rustlings operates:
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!
(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!
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!
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
@ -33,6 +31,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md
name = "intro1"
dir = "00_intro"
test = false
skip_check_unsolved = true
hint = """
Enter `n` to move on to the next exercise.
You might need to press ENTER after typing `n`."""
@ -130,8 +129,7 @@ The type of Constants must always be annotated.
Read more about constants and the differences between variables and constants
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
@ -312,8 +310,7 @@ In Rust, there are two ways to define a Vector.
the initial values.
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]]
name = "vecs2"
@ -327,8 +324,7 @@ 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
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
@ -355,8 +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."""
[[exercises]]
name = "move_semantics3"
@ -375,8 +370,7 @@ Carefully reason about the range in which each mutable reference is in
scope. Does it help to update the value of `x` immediately after
the mutable reference is taken?
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]]
name = "move_semantics5"
@ -517,8 +511,7 @@ Example:
`placeholder("blue");`
should become
`string_slice("blue");`
because "blue" is `&str`, not `String`.
"""
because "blue" is `&str`, not `String`."""
# MODULES
@ -620,8 +613,7 @@ Remember that `Option`s can be nested in if-let and while-let statements.
For example: `if let Some(Some(x)) = y`
Also see `Option::flatten`
"""
Also see `Option::flatten`"""
[[exercises]]
name = "options3"
@ -813,8 +805,7 @@ Here is how to specify a trait bound for an implementation block:
`impl<T: Trait1 + Trait2 + > for Foo<T> { }`
You may need this:
`use std::fmt::Display;`
"""
`use std::fmt::Display;`"""
# LIFETIMES

View file

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

View file

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

View file

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

View file

@ -1,3 +1,4 @@
#[allow(dead_code)]
mod delicious_snacks {
// Added `pub` and used the expected alias after `as`.
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();
// Insert the default with zeros if a team doesn't exist yet.
let mut team_1 = scores.entry(team_1_name).or_insert_with(|| Team::default());
let team_1 = scores.entry(team_1_name).or_insert_with(Team::default);
// Update the values.
team_1.goals_scored += team_1_score;
team_1.goals_conceded += team_2_score;
// Similarely for the second team.
let mut team_2 = scores.entry(team_2_name).or_insert_with(|| Team::default());
let team_2 = scores.entry(team_2_name).or_insert_with(Team::default);
team_2.goals_scored += team_2_score;
team_2.goals_conceded += team_1_score;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,5 @@
#![allow(dead_code)]
trait Licensed {
fn licensing_info(&self) -> 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
// different threads simultaneously. Each thread is going to get the sum of
// every eighth value with an offset.
@ -9,8 +9,11 @@
// …
// The eighth thread (offset 7), will sum 7, 15, 23, …
//
// Because we are using threads, our values need to be thread-safe. Therefore,
// we are using `Arc`.
// Each thread should own a reference-counting pointer to the vector of
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `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.
#![forbid(unused_imports)]

View file

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

View file

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

View file

@ -92,6 +92,10 @@ 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");
}
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]") {
bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file");
}
@ -158,7 +162,46 @@ fn check_unexpected_files(
Ok(())
}
fn check_exercises(info_file: &InfoFile) -> Result<()> {
fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> 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) {
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"),
@ -168,15 +211,14 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> {
let info_file_paths = check_info_file_exercises(info_file)?;
check_unexpected_files("exercises", &info_file_paths)?;
Ok(())
check_exercises_unsolved(info_file, target_dir)
}
fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> {
let target_dir = parse_target_dir()?;
fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> Result<()> {
let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len()));
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| {
for exercise_info in &info_file.exercises {
s.spawn(|| {
@ -202,7 +244,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()>
}
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) => {
paths.lock().unwrap().insert(PathBuf::from(path));
}
@ -238,8 +280,9 @@ pub fn check(require_solutions: bool) -> Result<()> {
check_cargo_toml(&info_file.exercises, &current_cargo_toml, b"")?;
}
check_exercises(&info_file)?;
check_solutions(require_solutions, &info_file)?;
let target_dir = parse_target_dir()?;
check_exercises(&info_file, &target_dir)?;
check_solutions(require_solutions, &info_file, &target_dir)?;
println!("\nEverything looks fine!");
@ -248,3 +291,6 @@ pub fn check(require_solutions: bool) -> Result<()> {
const SEPARATOR: &[u8] =
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,14 +11,17 @@ pub struct ExerciseInfo {
pub name: String,
/// Exercise's directory name inside the `exercises/` directory.
pub dir: Option<String>,
#[serde(default = "default_true")]
/// Run `cargo test` on the exercise.
#[serde(default = "default_true")]
pub test: bool,
/// Deny all Clippy warnings.
#[serde(default)]
pub strict_clippy: bool,
/// The exercise's hint to be shown to the user on request.
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)]
const fn default_true() -> bool {