mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-26 00:00:03 +03:00
Compare commits
11 commits
f48a347c8b
...
fd8c0d2ab4
Author | SHA1 | Date | |
---|---|---|---|
fd8c0d2ab4 | |||
f696d98270 | |||
a8b13f5a82 | |||
86fc573d7a | |||
f82e47f2af | |||
75a38fa38b | |||
ac62a3713c | |||
ea52c99560 | |||
7d4100ed8a | |||
c8d1d9c51f | |||
ab2eb3442e |
41
CHANGELOG.md
41
CHANGELOG.md
|
@ -1,3 +1,44 @@
|
|||
<a name="6.3.0"></a>
|
||||
|
||||
## 6.3.0 (2024-08-29)
|
||||
|
||||
### Added
|
||||
|
||||
- Add the following exercise lints:
|
||||
- `forbid(unsafe_code)`: You shouldn't write unsafe code in Rustlings.
|
||||
- `forbid(unstable_features)`: You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust.
|
||||
- `forbid(todo)`: You forgot a `todo!()`.
|
||||
- `forbid(empty_loop)`: This can only happen by mistake in Rustlings.
|
||||
- `deny(infinite_loop)`: No infinite loops are needed in Rustlings.
|
||||
- `deny(mem_forget)`: You shouldn't leak memory while still learning Rust.
|
||||
- Show a link to every exercise file in the list.
|
||||
- Add scroll padding in the list.
|
||||
- Break the help footer of the list into two lines when the terminal width isn't big enough.
|
||||
- Enable scrolling with the mouse in the list.
|
||||
- `dev check`: Show the progress of checks.
|
||||
- `dev check`: Check that the length of all exercise names is lower than 32.
|
||||
- `dev check`: Check if exercise contains no tests and isn't marked with `test = false`.
|
||||
|
||||
### Changed
|
||||
|
||||
- The compilation time when installing Rustlings is reduced.
|
||||
- Pressing `c` in the list for "continue on" now quits the list after setting the selected exercise as the current one.
|
||||
- Better highlighting of the solution file after an exercise is done.
|
||||
- Don't show the output of successful tests anymore. Instead, show the pretty output for tests.
|
||||
- Be explicit about `q` only quitting the list and not the whole program in the list.
|
||||
- Be explicit about `r` only resetting one exercise (the selected one) in the list.
|
||||
- Ignore the standard output of `git init`.
|
||||
- `threads3`: Remove the queue length and improve tests.
|
||||
- `errors4`: Use match instead of a comparison chain in the solution.
|
||||
- `functions3`: Only take `u8` to avoid using a too high number of iterations by mistake.
|
||||
- `dev check`: Always check with strict Clippy (warnings to errors) when checking the solutions.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix the error on some systems about too many open files during the final check of all exercises.
|
||||
- Fix the list when the terminal height is too low.
|
||||
- Restore the terminal after an error in the list.
|
||||
|
||||
<a name="6.2.0"></a>
|
||||
|
||||
## 6.2.0 (2024-08-09)
|
||||
|
|
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -482,7 +482,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustlings"
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
|
@ -499,7 +499,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustlings-macros"
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
|
|
|
@ -6,7 +6,7 @@ exclude = [
|
|||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
authors = [
|
||||
"Mo Bitar <mo8it@proton.me>", # https://github.com/mo8it
|
||||
"Liv <mokou@fastmail.com>", # https://github.com/shadows-withal
|
||||
|
@ -52,7 +52,7 @@ clap = { version = "4.5.16", features = ["derive"] }
|
|||
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
|
||||
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||
os_pipe = "1.2.1"
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" }
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" }
|
||||
serde_json = "1.0.127"
|
||||
serde.workspace = true
|
||||
toml_edit.workspace = true
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
format_version = 1
|
||||
|
||||
welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners!
|
||||
welcome_message = """
|
||||
Is this your first time? Don't worry, Rustlings is made for beginners!
|
||||
We are going to teach you a lot of things about Rust, but before we can
|
||||
get started, here are some notes about how Rustlings operates:
|
||||
|
||||
|
@ -10,15 +11,16 @@ get started, here are some notes about how Rustlings operates:
|
|||
and fix them!
|
||||
2. Make sure to have your editor open in the `rustlings/` directory. Rustlings
|
||||
will show you the path of the current exercise under the progress bar. Open
|
||||
the exercise file in your editor, fix errors and save the file. Rustlings will
|
||||
automatically detect the file change and rerun the exercise. If all errors are
|
||||
fixed, Rustlings will ask you to move on to the next exercise.
|
||||
the exercise file in your editor, fix errors and save the file. Rustlings
|
||||
will automatically detect the file change and rerun the exercise. If all
|
||||
errors are fixed, Rustlings will ask you to move on to the next exercise.
|
||||
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!"""
|
||||
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!"""
|
||||
|
||||
final_message = """We hope you enjoyed learning about the various aspects of Rust!
|
||||
final_message = """
|
||||
We hope you enjoyed learning about the various aspects of Rust!
|
||||
If you noticed any issues, don't hesitate to report them on Github.
|
||||
You can also contribute your own exercises to help the greater community!
|
||||
|
||||
|
@ -122,8 +124,8 @@ hint = """
|
|||
We know about variables and mutability, but there is another important type of
|
||||
variables available: constants.
|
||||
|
||||
Constants are always immutable. They are declared with the keyword `const` instead
|
||||
of `let`.
|
||||
Constants are always immutable. They are declared with the keyword `const`
|
||||
instead of `let`.
|
||||
|
||||
The type of Constants must always be annotated.
|
||||
|
||||
|
@ -253,7 +255,7 @@ require you to type in 100 items (but you certainly can if you want!).
|
|||
|
||||
For example, you can do:
|
||||
```
|
||||
let array = ["Are we there yet?"; 10];
|
||||
let array = ["Are we there yet?"; 100];
|
||||
```
|
||||
|
||||
Bonus: what are some other things you could have that would return `true`
|
||||
|
@ -319,7 +321,8 @@ hint = """
|
|||
In the first function, we create an empty vector and want to push new elements
|
||||
to it.
|
||||
|
||||
In the second function, we map the values of the input and collect them into a vector.
|
||||
In the second function, we map the values of the input and collect them into
|
||||
a vector.
|
||||
|
||||
After you've completed both functions, decide for yourself which approach you
|
||||
like better.
|
||||
|
@ -332,8 +335,8 @@ What do you think is the more commonly used pattern under Rust developers?"""
|
|||
name = "move_semantics1"
|
||||
dir = "06_move_semantics"
|
||||
hint = """
|
||||
So you've got the "cannot borrow `vec` as mutable, as it is not declared as mutable"
|
||||
error on the line where we push an element to the vector, right?
|
||||
So you've got the "cannot borrow `vec` as mutable, as it is not declared as
|
||||
mutable" error on the line where we push an element to the vector, right?
|
||||
|
||||
The fix for this is going to be adding one keyword, and the addition is NOT on
|
||||
the line where we push to the vector (where the error is).
|
||||
|
@ -369,7 +372,8 @@ hint = """
|
|||
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':
|
||||
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."""
|
||||
|
||||
[[exercises]]
|
||||
|
@ -508,7 +512,8 @@ name = "strings4"
|
|||
dir = "09_strings"
|
||||
test = false
|
||||
hint = """
|
||||
Replace `placeholder` with either `string` or `string_slice` in the `main` function.
|
||||
Replace `placeholder` with either `string` or `string_slice` in the `main`
|
||||
function.
|
||||
|
||||
Example:
|
||||
`placeholder("blue");`
|
||||
|
@ -1211,7 +1216,8 @@ hint = """
|
|||
Is there an implementation of `TryFrom` in the standard library that can both do
|
||||
the required integer conversion and check the range of the input?
|
||||
|
||||
Challenge: Can you make the `TryFrom` implementations generic over many integer types?"""
|
||||
Challenge: Can you make the `TryFrom` implementations generic over many integer
|
||||
types?"""
|
||||
|
||||
[[exercises]]
|
||||
name = "as_ref_mut"
|
||||
|
|
|
@ -331,7 +331,7 @@ impl AppState {
|
|||
})
|
||||
}
|
||||
|
||||
/// Official exercises: Dump the solution file form the binary and return its path.
|
||||
/// Official exercises: Dump the solution file from the binary and return its path.
|
||||
/// Third-party exercises: Check if a solution file exists and return its path in that case.
|
||||
pub fn current_solution_path(&self) -> Result<Option<String>> {
|
||||
if cfg!(debug_assertions) {
|
||||
|
|
|
@ -43,8 +43,6 @@ pub struct ListState<'a> {
|
|||
filter: Filter,
|
||||
term_width: u16,
|
||||
term_height: u16,
|
||||
separator_line: Vec<u8>,
|
||||
narrow_term: bool,
|
||||
show_footer: bool,
|
||||
}
|
||||
|
||||
|
@ -77,8 +75,6 @@ impl<'a> ListState<'a> {
|
|||
// Set by `set_term_size`
|
||||
term_width: 0,
|
||||
term_height: 0,
|
||||
separator_line: Vec::new(),
|
||||
narrow_term: false,
|
||||
show_footer: true,
|
||||
};
|
||||
|
||||
|
@ -96,19 +92,11 @@ impl<'a> ListState<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
let wide_help_footer_width = 95;
|
||||
// The help footer is shorter when nothing is selected.
|
||||
self.narrow_term = width < wide_help_footer_width && self.scroll_state.selected().is_some();
|
||||
|
||||
let header_height = 1;
|
||||
// 2 separator, 1 progress bar, 1-2 footer message.
|
||||
let footer_height = 4 + u16::from(self.narrow_term);
|
||||
// 1 progress bar, 2 footer message lines.
|
||||
let footer_height = 3;
|
||||
self.show_footer = height > header_height + footer_height;
|
||||
|
||||
if self.show_footer {
|
||||
self.separator_line = "─".as_bytes().repeat(width as usize);
|
||||
}
|
||||
|
||||
self.scroll_state.set_max_n_rows_to_display(
|
||||
height.saturating_sub(header_height + u16::from(self.show_footer) * footer_height)
|
||||
as usize,
|
||||
|
@ -208,9 +196,6 @@ impl<'a> ListState<'a> {
|
|||
}
|
||||
|
||||
if self.show_footer {
|
||||
stdout.write_all(&self.separator_line)?;
|
||||
next_ln(stdout)?;
|
||||
|
||||
progress_bar(
|
||||
&mut MaxLenWriter::new(stdout, self.term_width as usize),
|
||||
self.app_state.n_done(),
|
||||
|
@ -219,22 +204,15 @@ impl<'a> ListState<'a> {
|
|||
)?;
|
||||
next_ln(stdout)?;
|
||||
|
||||
stdout.write_all(&self.separator_line)?;
|
||||
next_ln(stdout)?;
|
||||
|
||||
let mut writer = MaxLenWriter::new(stdout, self.term_width as usize);
|
||||
if self.message.is_empty() {
|
||||
// Help footer message
|
||||
if self.scroll_state.selected().is_some() {
|
||||
writer.write_str("↓/j ↑/k home/g end/G | <c>ontinue at | <r>eset exercise")?;
|
||||
if self.narrow_term {
|
||||
next_ln(stdout)?;
|
||||
writer = MaxLenWriter::new(stdout, self.term_width as usize);
|
||||
next_ln(stdout)?;
|
||||
writer = MaxLenWriter::new(stdout, self.term_width as usize);
|
||||
|
||||
writer.write_ascii(b"filter ")?;
|
||||
} else {
|
||||
writer.write_ascii(b" | filter ")?;
|
||||
}
|
||||
writer.write_ascii(b"<s>earch | filter ")?;
|
||||
} else {
|
||||
// Nothing selected (and nothing shown), so only display filter and quit.
|
||||
writer.write_ascii(b"filter ")?;
|
||||
|
@ -263,17 +241,14 @@ impl<'a> ListState<'a> {
|
|||
}
|
||||
|
||||
writer.write_ascii(b" | <q>uit list")?;
|
||||
next_ln(stdout)?;
|
||||
} else {
|
||||
writer.stdout.queue(SetForegroundColor(Color::Magenta))?;
|
||||
writer.write_str(&self.message)?;
|
||||
stdout.queue(ResetColor)?;
|
||||
next_ln(stdout)?;
|
||||
|
||||
if self.narrow_term {
|
||||
next_ln(stdout)?;
|
||||
}
|
||||
}
|
||||
|
||||
next_ln(stdout)?;
|
||||
}
|
||||
|
||||
stdout.queue(EndSynchronizedUpdate)?.flush()
|
||||
|
|
14
src/term.rs
14
src/term.rs
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use crossterm::{
|
||||
cursor::MoveTo,
|
||||
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
|
||||
style::{Attribute, Color, SetAttribute, SetForegroundColor},
|
||||
terminal::{Clear, ClearType},
|
||||
Command, QueueableCommand,
|
||||
};
|
||||
|
@ -93,20 +93,19 @@ pub fn progress_bar<'a>(
|
|||
total: u16,
|
||||
line_width: u16,
|
||||
) -> io::Result<()> {
|
||||
debug_assert!(total < 1000);
|
||||
debug_assert!(progress <= total);
|
||||
|
||||
const PREFIX: &[u8] = b"Progress: [";
|
||||
const PREFIX_WIDTH: u16 = PREFIX.len() as u16;
|
||||
// Leaving the last char empty (_) for `total` > 99.
|
||||
const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16;
|
||||
const POSTFIX_WIDTH: u16 = "] xxx/xxx".len() as u16;
|
||||
const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH;
|
||||
const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4;
|
||||
|
||||
if line_width < MIN_LINE_WIDTH {
|
||||
writer.write_ascii(b"Progress: ")?;
|
||||
// Integers are in ASCII.
|
||||
writer.write_ascii(format!("{progress}/{total}").as_bytes())?;
|
||||
return writer.write_ascii(b" exercises");
|
||||
return writer.write_ascii(format!("{progress}/{total}").as_bytes());
|
||||
}
|
||||
|
||||
let stdout = writer.stdout();
|
||||
|
@ -133,8 +132,9 @@ pub fn progress_bar<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
stdout.queue(ResetColor)?;
|
||||
write!(stdout, "] {progress:>3}/{total} exercises")
|
||||
stdout.queue(SetForegroundColor(Color::Reset))?;
|
||||
|
||||
write!(stdout, "] {progress:>3}/{total}")
|
||||
}
|
||||
|
||||
pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> {
|
||||
|
|
Loading…
Reference in a new issue