mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-03-15 20:45:03 +00:00
Compare commits
No commits in common. "7e2f56f41a89213d3ae60a069402a25b570f0cca" and "0432e07864d5ee4d2bac1954c965b2077c0447c6" have entirely different histories.
7e2f56f41a
...
0432e07864
13 changed files with 62 additions and 37 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -186,6 +186,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
|
@ -277,9 +283,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.160"
|
||||
version = "0.2.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
|
@ -404,9 +410,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.88"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -449,6 +455,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"foldhash",
|
||||
"notify",
|
||||
"os_pipe",
|
||||
"rustix",
|
||||
|
|
|
@ -49,6 +49,7 @@ include = [
|
|||
anyhow = "1.0.89"
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
|
||||
foldhash = "0.1.3"
|
||||
notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] }
|
||||
os_pipe = "1.2.1"
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" }
|
||||
|
|
12
README.md
12
README.md
|
@ -124,13 +124,14 @@ The list allows you to…
|
|||
|
||||
- See the status of all exercises (done or pending)
|
||||
- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one)
|
||||
- `r`: Reset status and file of the selected exercise (you need to _reload/reopen_ its file in your editor afterwards)
|
||||
- `r`: Reset status and file of an exercise (you need to _reload/reopen_ its file in your editor afterwards)
|
||||
|
||||
See the footer of the list for all possible keys.
|
||||
|
||||
## Questions?
|
||||
## Continuing On
|
||||
|
||||
If you need any help while doing the exercises and the builtin-hints aren't helpful, feel free to ask in the [_Q&A_ category of the discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question wasn't asked yet 💡
|
||||
Once you've completed Rustlings, put your new knowledge to good use!
|
||||
Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to.
|
||||
|
||||
## Third-Party Exercises
|
||||
|
||||
|
@ -143,11 +144,6 @@ Do you want to create your own set of Rustlings exercises to focus on some speci
|
|||
Or do you want to translate the original Rustlings exercises?
|
||||
Then follow the the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)!
|
||||
|
||||
## Continuing On
|
||||
|
||||
Once you've completed Rustlings, put your new knowledge to good use!
|
||||
Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to.
|
||||
|
||||
## Uninstalling Rustlings
|
||||
|
||||
If you want to remove Rustlings from your system, run the following command:
|
||||
|
|
|
@ -5,6 +5,9 @@ disallowed-types = [
|
|||
]
|
||||
|
||||
disallowed-methods = [
|
||||
# We use `foldhash` instead of the default hasher.
|
||||
"std::collections::HashSet::new",
|
||||
"std::collections::HashSet::with_capacity",
|
||||
# Inefficient. Use `.queue(…)` instead.
|
||||
"crossterm::style::style",
|
||||
# Use `thread::Builder::spawn` instead and handle the error.
|
||||
|
|
|
@ -17,7 +17,7 @@ struct TeamScores {
|
|||
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
||||
// The name of the team is the key and its associated struct is the value.
|
||||
let mut scores = HashMap::<&str, TeamScores>::new();
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
for line in results.lines() {
|
||||
let mut split_iterator = line.split(',');
|
||||
|
|
|
@ -575,8 +575,12 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-
|
|||
name = "hashmaps3"
|
||||
dir = "11_hashmaps"
|
||||
hint = """
|
||||
Hint 1: Use the `entry()` and `or_default()` methods of `HashMap` to insert the
|
||||
default value of `TeamScores` if a team doesn't exist in the table yet.
|
||||
Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of
|
||||
`HashMap` to insert the default value of `TeamScores` if a team doesn't
|
||||
exist in the table yet.
|
||||
|
||||
Learn more in The Book:
|
||||
https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
||||
|
||||
Hint 2: If there is already an entry for a given key, the value returned by
|
||||
`entry()` can be updated based on the existing value.
|
||||
|
|
|
@ -17,7 +17,7 @@ struct TeamScores {
|
|||
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
||||
// The name of the team is the key and its associated struct is the value.
|
||||
let mut scores = HashMap::<&str, TeamScores>::new();
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
for line in results.lines() {
|
||||
let mut split_iterator = line.split(',');
|
||||
|
@ -28,13 +28,17 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
|||
let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
|
||||
|
||||
// Insert the default with zeros if a team doesn't exist yet.
|
||||
let team_1 = scores.entry(team_1_name).or_default();
|
||||
let team_1 = scores
|
||||
.entry(team_1_name)
|
||||
.or_insert_with(TeamScores::default);
|
||||
// Update the values.
|
||||
team_1.goals_scored += team_1_score;
|
||||
team_1.goals_conceded += team_2_score;
|
||||
|
||||
// Similarly for the second team.
|
||||
let team_2 = scores.entry(team_2_name).or_default();
|
||||
let team_2 = scores
|
||||
.entry(team_2_name)
|
||||
.or_insert_with(TeamScores::default);
|
||||
team_2.goals_scored += team_2_score;
|
||||
team_2.goals_conceded += team_1_score;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use anyhow::{bail, Context, Error, Result};
|
||||
use crossterm::{cursor, terminal, QueueableCommand};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
env,
|
||||
fs::{File, OpenOptions},
|
||||
io::{Read, Seek, StdoutLock, Write},
|
||||
|
@ -17,6 +16,7 @@ use std::{
|
|||
use crate::{
|
||||
clear_terminal,
|
||||
cmd::CmdRunner,
|
||||
collections::hash_set_with_capacity,
|
||||
embedded::EMBEDDED_FILES,
|
||||
exercise::{Exercise, RunnableExercise},
|
||||
info_file::ExerciseInfo,
|
||||
|
@ -146,7 +146,7 @@ impl AppState {
|
|||
break 'block StateFileStatus::NotRead;
|
||||
}
|
||||
|
||||
let mut done_exercises = HashSet::with_capacity(exercises.len());
|
||||
let mut done_exercises = hash_set_with_capacity(exercises.len());
|
||||
|
||||
for done_exercise_name in lines {
|
||||
if done_exercise_name.is_empty() {
|
||||
|
|
|
@ -125,7 +125,7 @@ pub struct CargoSubcommand<'out> {
|
|||
output: Option<&'out mut Vec<u8>>,
|
||||
}
|
||||
|
||||
impl CargoSubcommand<'_> {
|
||||
impl<'out> CargoSubcommand<'out> {
|
||||
#[inline]
|
||||
pub fn args<'arg, I>(&mut self, args: I) -> &mut Self
|
||||
where
|
||||
|
|
9
src/collections.rs
Normal file
9
src/collections.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use foldhash::fast::FixedState;
|
||||
|
||||
/// DOS attacks aren't a concern for Rustlings. Therefore, we use `foldhash` with a fixed state.
|
||||
pub type HashSet<T> = std::collections::HashSet<T, FixedState>;
|
||||
|
||||
#[inline]
|
||||
pub fn hash_set_with_capacity<T>(capacity: usize) -> HashSet<T> {
|
||||
HashSet::with_capacity_and_hasher(capacity, FixedState::default())
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::HashSet,
|
||||
fs::{self, read_dir, OpenOptions},
|
||||
io::{self, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
|
@ -12,6 +11,7 @@ use std::{
|
|||
use crate::{
|
||||
cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY},
|
||||
cmd::CmdRunner,
|
||||
collections::{hash_set_with_capacity, HashSet},
|
||||
exercise::{RunnableExercise, OUTPUT_CAPACITY},
|
||||
info_file::{ExerciseInfo, InfoFile},
|
||||
CURRENT_FORMAT_VERSION,
|
||||
|
@ -53,8 +53,8 @@ fn check_cargo_toml(
|
|||
|
||||
// Check the info of all exercises and return their paths in a set.
|
||||
fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
|
||||
let mut names = HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut paths = HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut names = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut paths = hash_set_with_capacity(info_file.exercises.len());
|
||||
|
||||
let mut file_buf = String::with_capacity(1 << 14);
|
||||
for exercise_info in &info_file.exercises {
|
||||
|
@ -282,7 +282,7 @@ fn check_solutions(
|
|||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("Failed to spawn a thread to check a solution")?;
|
||||
|
||||
let mut sol_paths = HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut sol_paths = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut fmt_cmd = Command::new("rustfmt");
|
||||
fmt_cmd
|
||||
.arg("--check")
|
||||
|
|
|
@ -13,6 +13,7 @@ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile};
|
|||
mod app_state;
|
||||
mod cargo_toml;
|
||||
mod cmd;
|
||||
mod collections;
|
||||
mod dev;
|
||||
mod embedded;
|
||||
mod exercise;
|
||||
|
|
24
src/term.rs
24
src/term.rs
|
@ -11,15 +11,15 @@ use std::{
|
|||
|
||||
use crate::app_state::CheckProgress;
|
||||
|
||||
pub struct MaxLenWriter<'a, 'lock> {
|
||||
pub stdout: &'a mut StdoutLock<'lock>,
|
||||
pub struct MaxLenWriter<'a, 'b> {
|
||||
pub stdout: &'a mut StdoutLock<'b>,
|
||||
len: usize,
|
||||
max_len: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'lock> MaxLenWriter<'a, 'lock> {
|
||||
impl<'a, 'b> MaxLenWriter<'a, 'b> {
|
||||
#[inline]
|
||||
pub fn new(stdout: &'a mut StdoutLock<'lock>, max_len: usize) -> Self {
|
||||
pub fn new(stdout: &'a mut StdoutLock<'b>, max_len: usize) -> Self {
|
||||
Self {
|
||||
stdout,
|
||||
len: 0,
|
||||
|
@ -34,13 +34,13 @@ impl<'a, 'lock> MaxLenWriter<'a, 'lock> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait CountedWrite<'lock> {
|
||||
pub trait CountedWrite<'a> {
|
||||
fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()>;
|
||||
fn write_str(&mut self, unicode: &str) -> io::Result<()>;
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'lock>;
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'a>;
|
||||
}
|
||||
|
||||
impl<'lock> CountedWrite<'lock> for MaxLenWriter<'_, 'lock> {
|
||||
impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> {
|
||||
fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> {
|
||||
let n = ascii.len().min(self.max_len.saturating_sub(self.len));
|
||||
if n > 0 {
|
||||
|
@ -65,7 +65,7 @@ impl<'lock> CountedWrite<'lock> for MaxLenWriter<'_, 'lock> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'lock> {
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'b> {
|
||||
self.stdout
|
||||
}
|
||||
}
|
||||
|
@ -87,17 +87,17 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct CheckProgressVisualizer<'a, 'lock> {
|
||||
stdout: &'a mut StdoutLock<'lock>,
|
||||
pub struct CheckProgressVisualizer<'a, 'b> {
|
||||
stdout: &'a mut StdoutLock<'b>,
|
||||
n_cols: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'lock> CheckProgressVisualizer<'a, 'lock> {
|
||||
impl<'a, 'b> CheckProgressVisualizer<'a, 'b> {
|
||||
const CHECKING_COLOR: Color = Color::Blue;
|
||||
const DONE_COLOR: Color = Color::Green;
|
||||
const PENDING_COLOR: Color = Color::Red;
|
||||
|
||||
pub fn build(stdout: &'a mut StdoutLock<'lock>, term_width: u16) -> io::Result<Self> {
|
||||
pub fn build(stdout: &'a mut StdoutLock<'b>, term_width: u16) -> io::Result<Self> {
|
||||
clear_terminal(stdout)?;
|
||||
stdout.write_all("Checking all exercises…\n".as_bytes())?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue