diff --git a/.all-contributorsrc b/.all-contributorsrc
index f2033dcd..94141140 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1542,6 +1542,177 @@
"contributions": [
"content"
]
+ },
+ {
+ "login": "rzrymiak",
+ "name": "rzrymiak",
+ "avatar_url": "https://avatars.githubusercontent.com/u/106121613?v=4",
+ "profile": "https://github.com/rzrymiak",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "miguelraz",
+ "name": "Miguel Raz Guzmán Macedo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/13056181?v=4",
+ "profile": "https://github.com/miguelraz",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "memark",
+ "name": "Magnus Markling",
+ "avatar_url": "https://avatars.githubusercontent.com/u/318504?v=4",
+ "profile": "https://github.com/memark",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "gasparitiago",
+ "name": "Tiago De Gaspari",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3237254?v=4",
+ "profile": "https://github.com/gasparitiago",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "skaunov",
+ "name": "skaunov",
+ "avatar_url": "https://avatars.githubusercontent.com/u/65976143?v=4",
+ "profile": "https://github.com/skaunov",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "cj81499",
+ "name": "Cal Jacobson",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9152032?v=4",
+ "profile": "http://caljacobson.dev",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "duchonic",
+ "name": "Duchoud Nicolas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34117620?v=4",
+ "profile": "https://github.com/duchonic",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "gfaugere",
+ "name": "Gaëtan Faugère",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11901979?v=4",
+ "profile": "https://github.com/gfaugere",
+ "contributions": [
+ "tool"
+ ]
+ },
+ {
+ "login": "bhbuehler",
+ "name": "bhbuehler",
+ "avatar_url": "https://avatars.githubusercontent.com/u/25541343?v=4",
+ "profile": "https://github.com/bhbuehler",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "nyurik",
+ "name": "Yuri Astrakhan",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1641515?v=4",
+ "profile": "https://github.com/nyurik",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "azzamsa",
+ "name": "azzamsa",
+ "avatar_url": "https://avatars.githubusercontent.com/u/17734314?v=4",
+ "profile": "http://azzamsa.com",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "mvanschellebeeck",
+ "name": "mvanschellebeeck",
+ "avatar_url": "https://avatars.githubusercontent.com/u/17671052?v=4",
+ "profile": "https://github.com/mvanschellebeeck",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "aaarkid",
+ "name": "Arkid",
+ "avatar_url": "https://avatars.githubusercontent.com/u/39987510?v=4",
+ "profile": "https://github.com/aaarkid",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "tfpk",
+ "name": "Tom Kunc",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10906982?v=4",
+ "profile": "http://tfpk.dev",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "mfurak",
+ "name": "Marek Furák",
+ "avatar_url": "https://avatars.githubusercontent.com/u/38523093?v=4",
+ "profile": "https://github.com/mfurak",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "winterqt",
+ "name": "Winter",
+ "avatar_url": "https://avatars.githubusercontent.com/u/78392041?v=4",
+ "profile": "https://winter.cafe",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "MoritzBoehme",
+ "name": "Moritz Böhme",
+ "avatar_url": "https://avatars.githubusercontent.com/u/42215704?v=4",
+ "profile": "https://moritzboeh.me",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "craymel",
+ "name": "craymel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/71062756?v=4",
+ "profile": "https://github.com/craymel",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "tkburis",
+ "name": "TK Buristrakul",
+ "avatar_url": "https://avatars.githubusercontent.com/u/20501289?v=4",
+ "profile": "https://github.com/tkburis",
+ "contributions": [
+ "content"
+ ]
}
],
"contributorsPerLine": 8,
@@ -1549,5 +1720,6 @@
"projectOwner": "rust-lang",
"repoType": "github",
"repoHost": "https://github.com",
- "skipCi": true
+ "skipCi": true,
+ "commitConvention": "angular"
}
diff --git a/.gitignore b/.gitignore
index 534453bc..c14f9227 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ exercises/clippy/Cargo.toml
exercises/clippy/Cargo.lock
rust-project.json
.idea
-.vscode
+.vscode/*
+!.vscode/extensions.json
*.iml
*.o
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000..b85de749
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "rust-lang.rust-analyzer"
+ ]
+}
diff --git a/AUTHORS.md b/AUTHORS.md
index 8edf09bf..2fc637f2 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -9,216 +9,243 @@ authors.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b15cf99..c351fce8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,50 @@
+
+## 5.3.0 (2022-12-23)
+
+#### Added
+
+- **cli**: Added a percentage display in watch mode
+- Added a `flake.nix` for Nix users
+
+#### Changed
+
+- **structs3**: Added an additional test
+- **macros**: Added a link to MacroKata in the README
+
+#### Fixed
+
+- **strings3**: Added a link to `std` in the hint
+- **threads1**: Corrected a hint link
+- **iterators1**: Clarified hint steps
+- **errors5**: Fix a typo in the hint
+- **options1**: Clarified on the usage of the 24-hour system
+- **threads2, threads3**: Explicitly use `Arc::clone`
+- **structs3**: Clarifed the hint
+- **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints
+- **traits1, traits2, cli**: Tidied up unmatching backticks
+- **enums2**: Removed unneccessary indirection of self
+- **enums3**: Added an extra tuple comment
+
+#### Housekeeping
+
+- Added a VSCode extension recommendation
+- Applied some Clippy and rustfmt formatting
+- Added a note on Windows PowerShell and other shell compatibility
+
+
+## 5.2.1 (2022-09-06)
+
+#### Fixed
+
+- **quiz1**: Reworded the comment to actually reflect what's going on in the tests.
+ Also added another assert just to make sure.
+- **rc1**: Fixed a typo in the hint.
+- **lifetimes**: Add quotes to the `println!` output, for readability.
+
+#### Housekeeping
+
+- Fixed a typo in README.md
+
## 5.2.0 (2022-08-27)
diff --git a/Cargo.lock b/Cargo.lock
index 2370e8dc..49f20b66 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -459,7 +459,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rustlings"
-version = "5.1.1"
+version = "5.3.0"
dependencies = [
"argh",
"assert_cmd",
diff --git a/Cargo.toml b/Cargo.toml
index 25cd7bfe..c2c54fd2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,10 @@
[package]
name = "rustlings"
-version = "5.2.0"
-authors = ["Liv ", "Carol (Nichols || Goulding) "]
+version = "5.3.0"
+authors = [
+ "Liv ",
+ "Carol (Nichols || Goulding) ",
+]
edition = "2021"
[dependencies]
diff --git a/README.md b/README.md
index 2392ba9d..956bb6d0 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,19 @@ curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh |
This will install Rustlings and give you access to the `rustlings` command. Run it to get started!
+### Nix
+Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`.
+
+```bash
+# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.3.0)
+git clone -b 5.3.0 --depth 1 https://github.com/rust-lang/rustlings
+cd rustlings
+# if nix version > 2.3
+nix develop
+# if nix version <= 2.3
+nix-shell
+```
+
## Windows
In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`:
@@ -42,7 +55,7 @@ Then, you can run:
Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1
```
-To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it.
+To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors.
If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus.
@@ -57,8 +70,8 @@ If you get a permission denied message, you might have to exclude the directory
Basically: Clone the repository at the latest tag, run `cargo install --path .`.
```bash
-# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.1.1)
-git clone -b 5.1.1 --depth 1 https://github.com/rust-lang/rustlings
+# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.3.0)
+git clone -b 5.3.0 --depth 1 https://github.com/rust-lang/rustlings
cd rustlings
cargo install --force --path .
```
@@ -126,7 +139,7 @@ After every couple of sections, there will be a quiz that'll test your knowledge
## Enabling `rust-analyzer`
-Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise.
+Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise.
## Continuing On
@@ -134,7 +147,7 @@ Once you've completed Rustlings, put your new knowledge to good use! Continue pr
## Uninstalling Rustlings
-If you want to remove Rustlings from your system, there's two steps. First, you'll need to remove the exercises folder that the install script created
+If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created
for you:
```bash
diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs
index 9f479739..e6a9d114 100644
--- a/exercises/conversions/as_ref_mut.rs
+++ b/exercises/conversions/as_ref_mut.rs
@@ -5,21 +5,22 @@
// I AM NOT DONE
-// Obtain the number of bytes (not characters) in the given argument
-// Add the AsRef trait appropriately as a trait bound
+// Obtain the number of bytes (not characters) in the given argument.
+// TODO: Add the AsRef trait appropriately as a trait bound.
fn byte_counter(arg: T) -> usize {
arg.as_ref().as_bytes().len()
}
-// Obtain the number of characters (not bytes) in the given argument
-// Add the AsRef trait appropriately as a trait bound
+// Obtain the number of characters (not bytes) in the given argument.
+// TODO: Add the AsRef trait appropriately as a trait bound.
fn char_counter(arg: T) -> usize {
arg.as_ref().chars().count()
}
-// Squares a number using AsMut. Add the trait bound as is appropriate and
-// implement the function body.
+// Squares a number using as_mut().
+// TODO: Add the appropriate trait bound.
fn num_sq(arg: &mut T) {
+ // TODO: Implement the function body.
???
}
diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs
index 18479f87..167a6b2e 100644
--- a/exercises/enums/enums2.rs
+++ b/exercises/enums/enums2.rs
@@ -10,7 +10,7 @@ enum Message {
impl Message {
fn call(&self) {
- println!("{:?}", &self);
+ println!("{:?}", self);
}
}
diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs
index 55acf6bc..54fd6f60 100644
--- a/exercises/enums/enums3.rs
+++ b/exercises/enums/enums3.rs
@@ -52,7 +52,7 @@ mod tests {
position: Point { x: 0, y: 0 },
color: (0, 0, 0),
};
- state.process(Message::ChangeColor((255, 0, 255)));
+ state.process(Message::ChangeColor((255, 0, 255))); // Remember: The extra parentheses mark a tuple type.
state.process(Message::Echo(String::from("hello world")));
state.process(Message::Move(Point { x: 10, y: 15 }));
state.process(Message::Quit);
diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs
index 2ba8f903..6da06ef3 100644
--- a/exercises/error_handling/errors5.rs
+++ b/exercises/error_handling/errors5.rs
@@ -7,7 +7,7 @@
// For now, think of the `Box` type as an "I want anything that does ???" type, which, given
// Rust's usual standards for runtime safety, should strike you as somewhat lenient!
-// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a
+// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a
// type which implements a particular trait. To do so, The Box is declared as of type Box where Trait is the trait
// the compiler looks for on any value used in that context. For this exercise, that context is the potential errors
// which can be returned in a Result.
@@ -46,7 +46,7 @@ impl PositiveNonzeroInteger {
match value {
x if x < 0 => Err(CreationError::Negative),
x if x == 0 => Err(CreationError::Zero),
- x => Ok(PositiveNonzeroInteger(x as u64))
+ x => Ok(PositiveNonzeroInteger(x as u64)),
}
}
}
diff --git a/exercises/error_handling/errors6.rs b/exercises/error_handling/errors6.rs
index 1306fb03..8097b490 100644
--- a/exercises/error_handling/errors6.rs
+++ b/exercises/error_handling/errors6.rs
@@ -16,7 +16,7 @@ use std::num::ParseIntError;
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
- ParseInt(ParseIntError)
+ ParseInt(ParseIntError),
}
impl ParsePosNonzeroError {
@@ -27,14 +27,11 @@ impl ParsePosNonzeroError {
// fn from_parseint...
}
-fn parse_pos_nonzero(s: &str)
- -> Result
-{
+fn parse_pos_nonzero(s: &str) -> Result {
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
let x: i64 = s.parse().unwrap();
- PositiveNonzeroInteger::new(x)
- .map_err(ParsePosNonzeroError::from_creation)
+ PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
}
// Don't change anything below this line.
@@ -53,7 +50,7 @@ impl PositiveNonzeroInteger {
match value {
x if x < 0 => Err(CreationError::Negative),
x if x == 0 => Err(CreationError::Zero),
- x => Ok(PositiveNonzeroInteger(x as u64))
+ x => Ok(PositiveNonzeroInteger(x as u64)),
}
}
}
diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs
index 18dd44c9..ad3baa68 100644
--- a/exercises/hashmaps/hashmaps3.rs
+++ b/exercises/hashmaps/hashmaps3.rs
@@ -37,7 +37,7 @@ fn build_scores_table(results: String) -> HashMap {
let team_2_score: u8 = v[3].parse().unwrap();
// TODO: Populate the scores table with details extracted from the
// current line. Keep in mind that goals scored by team_1
- // will be number of goals conceded from team_2, and similarly
+ // will be the number of goals conceded from team_2, and similarly
// goals scored by team_2 will be the number of goals conceded by
// team_1.
}
diff --git a/exercises/lifetimes/lifetimes1.rs b/exercises/lifetimes/lifetimes1.rs
index 58e995c6..0236470d 100644
--- a/exercises/lifetimes/lifetimes1.rs
+++ b/exercises/lifetimes/lifetimes1.rs
@@ -22,5 +22,5 @@ fn main() {
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
- println!("The longest string is {}", result);
+ println!("The longest string is '{}'", result);
}
diff --git a/exercises/lifetimes/lifetimes2.rs b/exercises/lifetimes/lifetimes2.rs
index c73a28ad..b48feabc 100644
--- a/exercises/lifetimes/lifetimes2.rs
+++ b/exercises/lifetimes/lifetimes2.rs
@@ -23,5 +23,5 @@ fn main() {
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
- println!("The longest string is {}", result);
+ println!("The longest string is '{}'", result);
}
diff --git a/exercises/macros/README.md b/exercises/macros/README.md
index 31a941b7..e34bc3a8 100644
--- a/exercises/macros/README.md
+++ b/exercises/macros/README.md
@@ -4,6 +4,10 @@ Rust's macro system is very powerful, but also kind of difficult to wrap your
head around. We're not going to teach you how to write your own fully-featured
macros. Instead, we'll show you how to use and create them.
+If you'd like to learn more about writing your own macros, the
+[macrokata](https://github.com/tfpk/macrokata) project has a similar style
+of exercises to Rustlings, but is all about learning to write Macros.
+
## Further information
- [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html)
diff --git a/exercises/options/options1.rs b/exercises/options/options1.rs
index d1735c2f..1f891b0e 100644
--- a/exercises/options/options1.rs
+++ b/exercises/options/options1.rs
@@ -6,10 +6,10 @@
// This function returns how much icecream there is left in the fridge.
// If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them
// all, so there'll be no more left :(
-// TODO: Return an Option!
fn maybe_icecream(time_of_day: u16) -> Option {
- // We use the 24-hour system here, so 10PM is a value of 22
- // The Option output should gracefully handle cases where time_of_day > 24.
+ // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0
+ // The Option output should gracefully handle cases where time_of_day > 23.
+ // TODO: Complete the function body - remember to return an Option!
???
}
diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs
index d1e76e59..dbb5cdc9 100644
--- a/exercises/quiz1.rs
+++ b/exercises/quiz1.rs
@@ -4,9 +4,11 @@
// - Functions
// - If
-// Mary is buying apples. One apple usually costs 2 Rustbucks, but if you buy
-// 40 or more at once, each apple only costs 1! Write a function that calculates
-// the price of an order of apples given the quantity bought. No hints this time!
+// 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. No hints this time!
// I AM NOT DONE
@@ -18,9 +20,11 @@
fn verify_test() {
let price1 = calculate_price_of_apples(35);
let price2 = calculate_price_of_apples(40);
- let price3 = calculate_price_of_apples(65);
+ let price3 = calculate_price_of_apples(41);
+ let price4 = calculate_price_of_apples(65);
assert_eq!(70, price1);
assert_eq!(80, price2);
- assert_eq!(65, price3);
+ assert_eq!(41, price3);
+ assert_eq!(65, price4);
}
diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs
index d8fa954a..715788b8 100644
--- a/exercises/quiz2.rs
+++ b/exercises/quiz2.rs
@@ -16,7 +16,7 @@
// - The input is going to be a Vector of a 2-length tuple,
// the first element is the string, the second one is the command.
// - The output element is going to be a Vector of strings.
-// Execute `rustlings hint quiz2` or use the `hint` watch subcommand for a hint.
+// No hints this time!
// I AM NOT DONE
@@ -42,7 +42,7 @@ mod my_module {
#[cfg(test)]
mod tests {
- // TODO: What do we have to import to have `transformer` in scope?
+ // TODO: What do we need to import to have `transformer` in scope?
use ???;
use super::Command;
diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs
index 0b3615f4..3536a457 100644
--- a/exercises/structs/structs3.rs
+++ b/exercises/structs/structs3.rs
@@ -78,5 +78,6 @@ mod tests {
let package = Package::new(sender_country, recipient_country, 1500);
assert_eq!(package.get_fees(cents_per_gram), 4500);
+ assert_eq!(package.get_fees(cents_per_gram * 2), 9000);
}
}
diff --git a/exercises/threads/threads2.rs b/exercises/threads/threads2.rs
index d0f8578f..ada3d14a 100644
--- a/exercises/threads/threads2.rs
+++ b/exercises/threads/threads2.rs
@@ -17,7 +17,7 @@ fn main() {
let status = Arc::new(JobStatus { jobs_completed: 0 });
let mut handles = vec![];
for _ in 0..10 {
- let status_shared = status.clone();
+ let status_shared = Arc::clone(&status);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(250));
// TODO: You must take an action before you update a shared value
diff --git a/exercises/threads/threads3.rs b/exercises/threads/threads3.rs
index 27e99088..9e9f285a 100644
--- a/exercises/threads/threads3.rs
+++ b/exercises/threads/threads3.rs
@@ -26,8 +26,8 @@ impl Queue {
fn send_tx(q: Queue, tx: mpsc::Sender) -> () {
let qc = Arc::new(q);
- let qc1 = qc.clone();
- let qc2 = qc.clone();
+ let qc1 = Arc::clone(&qc);
+ let qc2 = Arc::clone(&qc);
thread::spawn(move || {
for val in &qc1.first_half {
diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs
index 5b9d8d50..f5320a5a 100644
--- a/exercises/traits/traits1.rs
+++ b/exercises/traits/traits1.rs
@@ -2,7 +2,7 @@
// Time to implement some traits!
//
// Your task is to implement the trait
-// `AppendBar' for the type `String'.
+// `AppendBar` for the type `String`.
//
// The trait AppendBar has only one function,
// which appends "Bar" to any object
@@ -16,7 +16,7 @@ trait AppendBar {
}
impl AppendBar for String {
- //Add your code here
+ // TODO: Implement `AppendBar` for type `String`.
}
fn main() {
diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs
index 708bb19a..288b4983 100644
--- a/exercises/traits/traits2.rs
+++ b/exercises/traits/traits2.rs
@@ -1,7 +1,7 @@
// traits2.rs
//
// Your task is to implement the trait
-// `AppendBar' for a vector of strings.
+// `AppendBar` for a vector of strings.
//
// To implement this trait, consider for
// a moment what it means to 'append "Bar"'
@@ -17,7 +17,7 @@ trait AppendBar {
fn append_bar(self) -> Self;
}
-//TODO: Add your code here
+// TODO: Implement trait `AppendBar` for a vector of strings.
#[cfg(test)]
mod tests {
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 00000000..ceb62c6d
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,60 @@
+{
+ "nodes": {
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1650374568,
+ "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "b4a34015c698c7793d592d66adbab377907a2be8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1659877975,
+ "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1666629043,
+ "narHash": "sha256-Yoq6Ut2F3Ol73yO9hG93x6ts5c4F5BhKTbcF3DtBEAw=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "b39fd6e4edef83cb4a135ebef98751ce23becc33",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 00000000..a6703199
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,53 @@
+{
+ description = "Small exercises to get you used to reading and writing Rust code";
+
+ inputs = {
+ flake-compat = {
+ url = "github:edolstra/flake-compat";
+ flake = false;
+ };
+ flake-utils.url = "github:numtide/flake-utils";
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ };
+
+ outputs = { self, flake-utils, nixpkgs, ... }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ rustlings =
+ pkgs.rustPlatform.buildRustPackage {
+ name = "rustlings";
+ version = "5.3.0";
+
+ src = with pkgs.lib; cleanSourceWith {
+ src = self;
+ # a function that returns a bool determining if the path should be included in the cleaned source
+ filter = path: type:
+ let
+ # filename
+ baseName = builtins.baseNameOf (toString path);
+ # path from root directory
+ path' = builtins.replaceStrings [ "${self}/" ] [ "" ] path;
+ # checks if path is in the directory
+ inDirectory = directory: hasPrefix directory path';
+ in
+ inDirectory "src" ||
+ inDirectory "tests" ||
+ hasPrefix "Cargo" baseName ||
+ baseName == "info.toml";
+ };
+
+ cargoLock.lockFile = ./Cargo.lock;
+ };
+ in
+ {
+ devShell = pkgs.mkShell {
+ buildInputs = with pkgs; [
+ cargo
+ rustc
+ rust-analyzer
+ rustlings
+ ];
+ };
+ });
+}
diff --git a/info.toml b/info.toml
index 060643d7..8356f6ab 100644
--- a/info.toml
+++ b/info.toml
@@ -386,11 +386,9 @@ name = "structs3"
path = "exercises/structs/structs3.rs"
mode = "test"
hint = """
-The new method needs to panic if the weight is physically impossible :), how do we do that in Rust?
-
For is_international: What makes a package international? Seems related to the places it goes through right?
-For calculate_transport_fees: Bigger is more expensive usually, we don't have size, but something may fit the bill here :)
+For get_fees: This method takes an additional argument, is there a field in the Package struct that this relates to?
Have a look in The Book, to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html"""
@@ -416,8 +414,8 @@ path = "exercises/enums/enums3.rs"
mode = "test"
hint = """
As a first step, you can define enums to compile this code without errors.
-and then create a match expression in `process()`.
-Note that you need to deconstruct some message variants
+and then create a match expression in `process()`.
+Note that you need to deconstruct some message variants
in the match expression to get value in the variant."""
# STRINGS
@@ -449,7 +447,7 @@ path = "exercises/strings/strings3.rs"
mode = "test"
hint = """
There's tons of useful standard library functions for strings. Let's try and use some of
-them!
+them: !
For the compose_me method: You can either use the `format!` macro, or convert the string
slice into an owned string, which you can then freely extend."""
@@ -476,7 +474,7 @@ name = "modules2"
path = "exercises/modules/modules2.rs"
mode = "compile"
hint = """
-The delicious_snacks module is trying to present an external interface that is
+The delicious_snacks module is trying to present an external interface that is
different than its internal structure (the `fruits` and `veggies` modules and
associated constants). Complete the `use` statements to fit the uses in main and
find the one keyword missing for both constants."""
@@ -623,12 +621,12 @@ path = "exercises/error_handling/errors5.rs"
mode = "compile"
hint = """
There are two different possible `Result` types produced within `main()`, which are
-propagated using `?` operators. How do we declare a return type from `main()` that allows both?
+propagated using `?` operators. How do we declare a return type from `main()` that allows both?
Under the hood, the `?` operator calls `From::from` on the error value to convert it to a boxed
trait object, a `Box`. This boxed trait object is polymorphic, and since all
-errors implement the `error:Error` trait, we can capture lots of different errors in one "Box"
-object.
+errors implement the `error::Error` trait, we can capture lots of different errors in one "Box"
+object.
Check out this section of the book:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
@@ -667,7 +665,7 @@ name = "generics1"
path = "exercises/generics/generics1.rs"
mode = "compile"
hint = """
-Vectors in rust make use of generics to create dynamically sized arrays of any type.
+Vectors in Rust make use of generics to create dynamically sized arrays of any type.
You need to tell the compiler what type we are pushing onto this vector."""
[[exercises]]
@@ -697,7 +695,7 @@ name = "traits2"
path = "exercises/traits/traits2.rs"
mode = "test"
hint = """
-Notice how the trait takes ownership of 'self',and returns `Self'.
+Notice how the trait takes ownership of 'self',and returns `Self`.
Try mutating the incoming string vector. Have a look at the tests to see
what the result should look like!
@@ -818,9 +816,9 @@ Step 1:
We need to apply something to the collection `my_fav_fruits` before we start to go through
it. What could that be? Take a look at the struct definition for a vector for inspiration:
https://doc.rust-lang.org/std/vec/struct.Vec.html.
-Step 2 & step 2.1:
+Step 2 & step 3:
Very similar to the lines above and below. You've got this!
-Step 3:
+Step 4:
An iterator goes through all elements in a collection, but what if we've run out of
elements? What should we expect here? If you're stuck, take a look at
https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.
@@ -862,7 +860,7 @@ case is a vector of integers and the failure case is a DivisionError.
The list_of_results function needs to return a vector of results.
-See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how
+See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how
the `FromIterator` trait is used in `collect()`. This trait is REALLY powerful! It
can make the solution to this exercise infinitely easier."""
@@ -943,7 +941,7 @@ After using drop() to move the Planets out of scope individually, the reference
In the end the sun only has one reference again, to itself. See more at:
https://doc.rust-lang.org/book/ch15-04-rc.html
-* Unforunately Pluto is no longer considered a planet :(
+* Unfortunately Pluto is no longer considered a planet :(
"""
[[exercises]]
@@ -964,12 +962,12 @@ name = "threads1"
path = "exercises/threads/threads1.rs"
mode = "compile"
hint = """
-`JoinHandle` is a struct that is returned from a spawned thread:
+`JoinHandle` is a struct that is returned from a spawned thread:
https://doc.rust-lang.org/std/thread/fn.spawn.html
-A challenge with multi-threaded applications is that the main thread can
+A challenge with multi-threaded applications is that the main thread can
finish before the spawned threads are completed.
-https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handle
+https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles
Collect the JoinHandles and wait for them to finish.
https://doc.rust-lang.org/std/thread/struct.JoinHandle.html
@@ -1073,11 +1071,11 @@ path = "exercises/clippy/clippy1.rs"
mode = "clippy"
hint = """
Rust stores the highest precision version of any long or inifinite precision
-mathematical constants in the rust standard library.
+mathematical constants in the Rust standard library.
https://doc.rust-lang.org/stable/std/f32/consts/index.html
We may be tempted to use our own approximations for certain mathematical constants,
-but clippy recognizes those imprecise mathematical constants as a source of
+but clippy recognizes those imprecise mathematical constants as a source of
potential error.
See the suggestions of the clippy warning in compile output and use the
appropriate replacement constant from std::f32::consts..."""
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 00000000..fa2a56c7
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,6 @@
+(import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock);
+in fetchTarball {
+ url =
+ "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
+ sha256 = lock.nodes.flake-compat.locked.narHash;
+}) { src = ./.; }).shellNix
diff --git a/src/exercise.rs b/src/exercise.rs
index 4be3a2cc..c0dae34e 100644
--- a/src/exercise.rs
+++ b/src/exercise.rs
@@ -20,7 +20,7 @@ fn temp_file() -> String {
.filter(|c| c.is_alphanumeric())
.collect();
- format!("./temp_{}_{}", process::id(), thread_id)
+ format!("./temp_{}_{thread_id}", process::id())
}
// The mode of the exercise.
diff --git a/src/main.rs b/src/main.rs
index 8eebc086..6dc18e8b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -26,7 +26,7 @@ mod run;
mod verify;
// In sync with crate version
-const VERSION: &str = "5.2.0";
+const VERSION: &str = "5.3.0";
#[derive(FromArgs, PartialEq, Debug)]
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
@@ -121,12 +121,12 @@ fn main() {
let args: Args = argh::from_env();
if args.version {
- println!("v{}", VERSION);
+ println!("v{VERSION}");
std::process::exit(0);
}
if args.nested.is_none() {
- println!("\n{}\n", WELCOME);
+ println!("\n{WELCOME}\n");
}
if !Path::new("info.toml").exists() {
@@ -150,7 +150,7 @@ fn main() {
let verbose = args.nocapture;
let command = args.nested.unwrap_or_else(|| {
- println!("{}\n", DEFAULT_OUT);
+ println!("{DEFAULT_OUT}\n");
std::process::exit(0);
});
match command {
@@ -179,11 +179,11 @@ fn main() {
};
if solve_cond && (filter_cond || subargs.filter.is_none()) {
let line = if subargs.paths {
- format!("{}\n", fname)
+ format!("{fname}\n")
} else if subargs.names {
format!("{}\n", e.name)
} else {
- format!("{:<17}\t{:<46}\t{:<7}\n", e.name, fname, status)
+ format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name)
};
// Somehow using println! leads to the binary panicking
// when its output is piped.
@@ -202,7 +202,7 @@ fn main() {
});
let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0;
println!(
- "Progress: You completed {} / {} exercises ({:.2} %).",
+ "Progress: You completed {} / {} exercises ({:.1} %).",
exercises_done,
exercises.len(),
percentage_progress
@@ -266,7 +266,7 @@ fn main() {
"{emoji} All exercises completed! {emoji}",
emoji = Emoji("🎉", "★")
);
- println!("\n{}\n", FENISH_LINE);
+ println!("\n{FENISH_LINE}\n");
}
Ok(WatchStatus::Unfinished) => {
println!("We hope you're enjoying learning about Rust!");
@@ -289,7 +289,7 @@ fn spawn_watch_shell(
let input = input.trim();
if input == "hint" {
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
- println!("{}", hint);
+ println!("{hint}");
}
} else if input == "clear" {
println!("\x1B[2J\x1B[1;1H");
@@ -306,10 +306,10 @@ fn spawn_watch_shell(
println!("Watch mode automatically re-evaluates the current exercise");
println!("when you edit a file's contents.")
} else {
- println!("unknown command: {}", input);
+ println!("unknown command: {input}");
}
}
- Err(error) => println!("error reading command: {}", error),
+ Err(error) => println!("error reading command: {error}"),
}
});
}
@@ -329,7 +329,7 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
.iter()
.find(|e| e.name == name)
.unwrap_or_else(|| {
- println!("No exercise found for '{}'!", name);
+ println!("No exercise found for '{name}'!");
std::process::exit(1)
})
}
@@ -392,7 +392,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result {
Err(RecvTimeoutError::Timeout) => {
// the timeout expired, just check the `should_quit` variable below then loop again
}
- Err(e) => println!("watch error: {:?}", e),
+ Err(e) => println!("watch error: {e:?}"),
}
// Check if we need to exit
if should_quit.load(Ordering::SeqCst) {
diff --git a/src/run.rs b/src/run.rs
index 826f00a6..1e2e56cf 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -35,7 +35,7 @@ pub fn reset(exercise: &Exercise) -> Result<(), ()> {
// This is strictly for non-test binaries, so output is displayed
fn compile_and_run(exercise: &Exercise) -> Result<(), ()> {
let progress_bar = ProgressBar::new_spinner();
- progress_bar.set_message(format!("Compiling {}...", exercise));
+ progress_bar.set_message(format!("Compiling {exercise}..."));
progress_bar.enable_steady_tick(100);
let compilation_result = exercise.compile();
@@ -52,7 +52,7 @@ fn compile_and_run(exercise: &Exercise) -> Result<(), ()> {
}
};
- progress_bar.set_message(format!("Running {}...", exercise));
+ progress_bar.set_message(format!("Running {exercise}..."));
let result = compilation.run();
progress_bar.finish_and_clear();
diff --git a/src/verify.rs b/src/verify.rs
index 6f877831..97471b8f 100644
--- a/src/verify.rs
+++ b/src/verify.rs
@@ -16,7 +16,7 @@ pub fn verify<'a>(
let (num_done, total) = progress;
let bar = ProgressBar::new(total as u64);
bar.set_style(ProgressStyle::default_bar()
- .template("Progress: [{bar:60.green/red}] {pos}/{len}")
+ .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}")
.progress_chars("#>-")
);
bar.set_position(num_done as u64);
@@ -29,6 +29,8 @@ pub fn verify<'a>(
if !compile_result.unwrap_or(false) {
return Err(exercise);
}
+ let percentage = num_done as f32 / total as f32 * 100.0;
+ bar.set_message(format!("({:.1} %)", percentage));
bar.inc(1);
}
Ok(())
@@ -48,7 +50,7 @@ pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
// Invoke the rust compiler without running the resulting binary
fn compile_only(exercise: &Exercise) -> Result {
let progress_bar = ProgressBar::new_spinner();
- progress_bar.set_message(format!("Compiling {}...", exercise));
+ progress_bar.set_message(format!("Compiling {exercise}..."));
progress_bar.enable_steady_tick(100);
let _ = compile(exercise, &progress_bar)?;
@@ -60,12 +62,12 @@ fn compile_only(exercise: &Exercise) -> Result {
// Compile the given Exercise and run the resulting binary in an interactive mode
fn compile_and_run_interactively(exercise: &Exercise) -> Result {
let progress_bar = ProgressBar::new_spinner();
- progress_bar.set_message(format!("Compiling {}...", exercise));
+ progress_bar.set_message(format!("Compiling {exercise}..."));
progress_bar.enable_steady_tick(100);
let compilation = compile(exercise, &progress_bar)?;
- progress_bar.set_message(format!("Running {}...", exercise));
+ progress_bar.set_message(format!("Running {exercise}..."));
let result = compilation.run();
progress_bar.finish_and_clear();
@@ -86,7 +88,7 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result {
// the output if verbose is set to true
fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Result {
let progress_bar = ProgressBar::new_spinner();
- progress_bar.set_message(format!("Testing {}...", exercise));
+ progress_bar.set_message(format!("Testing {exercise}..."));
progress_bar.enable_steady_tick(100);
let compilation = compile(exercise, &progress_bar)?;
@@ -165,16 +167,16 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) ->
println!();
if no_emoji {
- println!("~*~ {} ~*~", success_msg)
+ println!("~*~ {success_msg} ~*~")
} else {
- println!("🎉 🎉 {} 🎉 🎉", success_msg)
+ println!("🎉 🎉 {success_msg} 🎉 🎉")
}
println!();
if let Some(output) = prompt_output {
println!("Output:");
println!("{}", separator());
- println!("{}", output);
+ println!("{output}");
println!("{}", separator());
println!();
}