mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-01-14 00:00:02 +03:00
Compare commits
22 commits
0f71e12235
...
47f8199a99
Author | SHA1 | Date | |
---|---|---|---|
47f8199a99 | |||
4bf0ddc0e1 | |||
4cd0ccce83 | |||
a3657188b6 | |||
b8fcd11286 | |||
4810555038 | |||
84b291852c | |||
74831dd88f | |||
0b220f9fff | |||
d3cdeed871 | |||
0524632199 | |||
30d5b7db92 | |||
2f60f4d9ea | |||
b017b87866 | |||
b87aa98634 | |||
a4c07ca948 | |||
b8826dd3b3 | |||
d54c050985 | |||
248dd4415e | |||
dec6772b05 | |||
b4f4c79ac4 | |||
c1d5252b87 |
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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
62
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
@ -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:?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ???;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(clippy::comparison_chain)]
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::rc::Rc;
|
|||
#[derive(Debug)]
|
||||
struct Sun;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum Planet {
|
||||
Mercury(Rc<Sun>),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(???) -> ??? {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(clippy::ptr_arg)]
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
|
|
|
@ -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:?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(clippy::comparison_chain)]
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
trait Licensed {
|
||||
fn licensing_info(&self) -> String {
|
||||
"Default license".to_string()
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::rc::Rc;
|
|||
#[derive(Debug)]
|
||||
struct Sun;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum Planet {
|
||||
Mercury(Rc<Sun>),
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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, ¤t_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.";
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue