mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-26 00:11:49 +02:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eac07df96c | ||
|
|
8c1376df07 | ||
|
|
45d68d9b39 | ||
|
|
c371b853af | ||
|
|
bc32a63c69 | ||
|
|
4b26546589 | ||
|
|
bc56788fe6 | ||
|
|
b4d41c1b7a | ||
|
|
36a033b87a | ||
|
|
9544ba1029 | ||
|
|
1a7bb5a400 | ||
|
|
48c35bcfbc | ||
|
|
ec2d4bd3ee | ||
|
|
ce9fa6ebbf | ||
|
|
9a9007abae | ||
|
|
88ec6f6b16 | ||
|
|
795b6e3480 | ||
|
|
9bdb0a12e4 | ||
|
|
2cdd61294f | ||
|
|
627cdc07d0 | ||
|
|
a47a62172a | ||
|
|
f72e5a8f05 | ||
|
|
fbe91a67a4 | ||
|
|
6dcecb38a4 | ||
|
|
dcfb427b09 | ||
|
|
ad03d180c9 | ||
|
|
3232a4d60d | ||
|
|
3aff590855 |
7
.editorconfig
Normal file
7
.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*.rs]
|
||||
end_of_line = lf
|
||||
insert_final_newfile = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,3 +1,36 @@
|
||||
<a name="2.0.0"></a>
|
||||
## 2.0.0 (2019-11-12)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **default:** Clarify the installation procedure ([c371b853](https://github.com/rust-lang/rustlings/commit/c371b853afa08947ddeebec0edd074b171eeaae0))
|
||||
* **info:** Fix trailing newlines for hints ([795b6e34](https://github.com/rust-lang/rustlings/commit/795b6e348094a898e9227a14f6232f7bb94c8d31))
|
||||
* **run:** make `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3))
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* Refactor hint system ([9bdb0a12](https://github.com/rust-lang/rustlings/commit/9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60))
|
||||
* improve `watch` execution mode ([2cdd6129](https://github.com/rust-lang/rustlings/commit/2cdd61294f0d9a53775ee24ad76435bec8a21e60))
|
||||
* Index exercises by name ([627cdc07](https://github.com/rust-lang/rustlings/commit/627cdc07d07dfe6a740e885e0ddf6900e7ec336b))
|
||||
* **run:** makes `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3))
|
||||
|
||||
#### Features
|
||||
|
||||
* **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425))
|
||||
* **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f))
|
||||
|
||||
<a name="1.5.1"></a>
|
||||
### 1.5.1 (2019-11-11)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **errors3:** Update hint ([dcfb427b](https://github.com/rust-lang/rustlings/commit/dcfb427b09585f0193f0a294443fdf99f11c64cb), closes [#185](https://github.com/rust-lang/rustlings/issues/185))
|
||||
* **if1:** Remove `return` reference ([ad03d180](https://github.com/rust-lang/rustlings/commit/ad03d180c9311c0093e56a3531eec1a9a70cdb45))
|
||||
* **strings:** Move Strings before Structs ([6dcecb38](https://github.com/rust-lang/rustlings/commit/6dcecb38a4435593beb87c8e12d6314143631482), closes [#204](https://github.com/rust-lang/rustlings/issues/204))
|
||||
* **structs1:** Remove misleading comment ([f72e5a8f](https://github.com/rust-lang/rustlings/commit/f72e5a8f05568dde04eaeac10b9a69872f21cb37))
|
||||
* **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205))
|
||||
* **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648))
|
||||
|
||||
<a name="1.5.0"></a>
|
||||
## 1.5.0 (2019-11-09)
|
||||
|
||||
|
||||
@@ -35,8 +35,11 @@ You want to make sure where in the file you add your exercise. If you're not sur
|
||||
```diff
|
||||
...
|
||||
+ [[exercises]]
|
||||
+ name = "yourTopicN"
|
||||
+ path = "exercises/yourTopic/yourTopicN.rs"
|
||||
+ mode = "compile"
|
||||
+ hint = """
|
||||
+ Some kind of useful hint for your exercise."""
|
||||
...
|
||||
```
|
||||
|
||||
@@ -125,4 +128,4 @@ BREAKING CHANGE: This has to be done because lorem ipsum dolor
|
||||
Once you open a Pull Request, it may be reviewed or labeled (or both) until
|
||||
the maintainers accept your change. Then, [bors](https://github.com/bors) will
|
||||
run the test suite with your changes and if it's successful, automatically
|
||||
merge it in!
|
||||
merge it in!
|
||||
|
||||
38
Cargo.lock
generated
38
Cargo.lock
generated
@@ -168,6 +168,14 @@ dependencies = [
|
||||
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent"
|
||||
version = "0.4.0"
|
||||
@@ -204,6 +212,11 @@ name = "fuchsia-zircon-sys"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.9.0"
|
||||
@@ -346,6 +359,11 @@ dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "4.0.12"
|
||||
@@ -364,6 +382,14 @@ dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
@@ -400,7 +426,10 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"normalize-line-endings 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -600,13 +629,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustlings"
|
||||
version = "1.4.1"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"console 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -843,11 +875,13 @@ dependencies = [
|
||||
"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd"
|
||||
"checksum escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597"
|
||||
"checksum filetime 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2f8c63033fcba1f51ef744505b3cad42510432b904c062afa67ad7ece008429d"
|
||||
"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600"
|
||||
"checksum fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
|
||||
"checksum fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
"checksum indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a29b2fa6f00010c268bface64c18bb0310aaa70d46a195d5382d288c477fb016"
|
||||
"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
|
||||
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
|
||||
@@ -865,7 +899,9 @@ dependencies = [
|
||||
"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
|
||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum normalize-line-endings 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2e0a1a39eab95caf4f5556da9289b9e68f0aafac901b2ce80daaf020d3b733a8"
|
||||
"checksum notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "3572d71f13ea8ed41867accd971fd564aa75934cf7a1fae03ddb8c74a8a49943"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7"
|
||||
"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustlings"
|
||||
version = "1.5.0"
|
||||
version = "2.0.0"
|
||||
authors = ["Marisa <mokou@posteo.de>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -10,6 +10,7 @@ indicatif = "0.9.0"
|
||||
console = "0.6.2"
|
||||
notify = "4.0.0"
|
||||
toml = "0.4.10"
|
||||
regex = "1.1.6"
|
||||
serde = {version = "1.0.10", features = ["derive"]}
|
||||
|
||||
[[bin]]
|
||||
@@ -18,3 +19,5 @@ path = "src/main.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "0.11.0"
|
||||
predicates = "1.0.1"
|
||||
glob = "0.3.0"
|
||||
|
||||
11
README.md
11
README.md
@@ -48,7 +48,7 @@ Basically: Clone the repository, checkout to the latest tag, run `cargo install`
|
||||
```bash
|
||||
git clone https://github.com/rust-lang/rustlings
|
||||
cd rustlings
|
||||
git checkout tags/1.5.0 # or whatever the latest version is (find out at https://github.com/rust-lang/rustlings/releases/latest)
|
||||
git checkout tags/2.0.0 # or whatever the latest version is (find out at https://github.com/rust-lang/rustlings/releases/latest)
|
||||
cargo install --force --path .
|
||||
```
|
||||
|
||||
@@ -80,10 +80,15 @@ This will do the same as watch, but it'll quit after running.
|
||||
In case you want to go by your own order, or want to only verify a single exercise, you can run:
|
||||
|
||||
```bash
|
||||
rustlings run exercises/path/to/exercise.rs
|
||||
rustlings run myExercise1
|
||||
```
|
||||
|
||||
In case you get stuck, there is usually a hint at the bottom of each exercise.
|
||||
In case you get stuck, you can run the following command to get a hint for your
|
||||
exercise:
|
||||
|
||||
``` bash
|
||||
rustlings hint myExercise1
|
||||
```
|
||||
|
||||
## Testing yourself
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ Let's make sure you're up to speed:
|
||||
- You have `~/.cargo/bin` added to your PATH variable
|
||||
- You have cloned this repository (https://github.com/rust-lang/rustlings)
|
||||
- You have installed Rust language support for your editor
|
||||
- You have locally installed the `rustlings` command by running:
|
||||
- You have locally installed the `rustlings` command by running an
|
||||
installation script or manually executing:
|
||||
|
||||
cargo install --force --path .
|
||||
|
||||
@@ -15,4 +16,4 @@ If you've done all of this (or even most of it), congrats! You're ready
|
||||
to start working with Rust.
|
||||
|
||||
To get started, run `rustlings watch` in order to get the first exercise.
|
||||
Make sure to have your editor open!
|
||||
Make sure to have your editor open!
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// enums1.rs
|
||||
// Make me compile! Scroll down for hints!
|
||||
// Make me compile! Execute `rustlings hint enums1` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
@@ -12,31 +14,3 @@ fn main() {
|
||||
println!("{:?}", Message::Move);
|
||||
println!("{:?}", Message::ChangeColor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Hint: The declaration of the enumeration type has not been defined yet.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// enums2.rs
|
||||
// Make me compile! Scroll down for hints
|
||||
// Make me compile! Execute `rustlings hint enums2` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
@@ -24,38 +26,3 @@ fn main() {
|
||||
message.call();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Hint: you can create enumerations that have different variants with different types
|
||||
// such as no data, anonymous structs, a single string, tuples, ...etc
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// enums3.rs
|
||||
// Address all the TODOs to make the tests pass!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
enum Message {
|
||||
// TODO: implement the message variant types based on their usage below
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// was, instead of just sometimes returning `None`. The 2nd test currently
|
||||
// does not compile or pass, but it illustrates the behavior we would like
|
||||
// this function to have.
|
||||
// Scroll down for hints!!!
|
||||
// Execute `rustlings hint errors1` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn generate_nametag_text(name: String) -> Option<String> {
|
||||
if name.len() > 0 {
|
||||
@@ -38,36 +40,3 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// `Err` is one of the variants of `Result`, so what the 2nd test is saying
|
||||
// is that `generate_nametag_text` should return a `Result` instead of an
|
||||
// `Option`.
|
||||
|
||||
// To make this change, you'll need to:
|
||||
// - update the return type in the function signature to be a Result<String, String> that
|
||||
// could be the variants `Ok(String)` and `Err(String)`
|
||||
// - change the body of the function to return `Ok(stuff)` where it currently
|
||||
// returns `Some(stuff)`
|
||||
// - change the body of the function to return `Err(error message)` where it
|
||||
// currently returns `None`
|
||||
// - change the first test to expect `Ok(stuff)` where it currently expects
|
||||
// `Some(stuff)`.
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
// and add.
|
||||
|
||||
// There are at least two ways to implement this that are both correct-- but
|
||||
// one is a lot shorter! Scroll down for hints to both ways.
|
||||
// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
@@ -43,27 +45,3 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// One way to handle this is using a `match` statement on
|
||||
// `item_quantity.parse::<i32>()` where the cases are `Ok(something)` and
|
||||
// `Err(something)`. This pattern is very common in Rust, though, so there's
|
||||
// a `?` operator that does pretty much what you would make that match statement
|
||||
// do for you! Take a look at this section of the Error Handling chapter:
|
||||
// https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
|
||||
// and give it a try!
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// errors3.rs
|
||||
// This is a program that is trying to use a completed version of the
|
||||
// `total_cost` function from the previous exercise. It's not working though--
|
||||
// we can't use the `?` operator in the `main()` function! Why not?
|
||||
// What should we do instead? Scroll for hints!
|
||||
// `total_cost` function from the previous exercise. It's not working though!
|
||||
// Why not? What should we do to fix it?
|
||||
// Execute `rustlings hint errors3` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
@@ -27,36 +29,3 @@ pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||
|
||||
Ok(qty * cost_per_item + processing_fee)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Since the `?` operator returns an `Err` early if the thing it's trying to
|
||||
// do fails, you can only use the `?` operator in functions that have a
|
||||
// `Result` as their return type.
|
||||
|
||||
// Hence the error that you get if you run this code is:
|
||||
|
||||
// ```
|
||||
// error[E0277]: the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`)
|
||||
// ```
|
||||
|
||||
// So we have to use another way of handling a `Result` within `main`.
|
||||
|
||||
// Decide what we should do if `pretend_user_input` has a string value that does
|
||||
// not parse to an integer, and implement that instead of using the `?`
|
||||
// operator.
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
// type goes where the question marks are, and how do we return
|
||||
// that type from the body of read_and_validate?
|
||||
//
|
||||
// Scroll down for hints :)
|
||||
// Execute `rustlings hint errorsn` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
@@ -110,138 +112,3 @@ impl error::Error for CreationError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// First hint: To figure out what type should go where the ??? is, take a look
|
||||
// at the test helper function `test_with_str`, since it returns whatever
|
||||
// `read_and_validate` returns and`test_with_str` has its signature fully
|
||||
// specified.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Next hint: There are three places in `read_and_validate` that we call a
|
||||
// function that returns a `Result` (that is, the functions might fail).
|
||||
// Apply the `?` operator on those calls so that we return immediately from
|
||||
// `read_and_validate` if those function calls fail.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Another hint: under the hood, the `?` operator calls `From::from`
|
||||
// on the error value to convert it to a boxed trait object, a Box<dyn error::Error>,
|
||||
// which is polymorphic-- that means that lots of different kinds of errors
|
||||
// can be returned from the same function because all errors act the same
|
||||
// since they all implement the `error::Error` trait.
|
||||
// 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Another another hint: Note that because the `?` operator returns
|
||||
// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
|
||||
// `read_and_validate` for *its* success case, we'll have to rewrap a value
|
||||
// that we got from the return value of a `?`ed call in an `Ok`-- this will
|
||||
// look like `Ok(something)`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Another another another hint: `Result`s must be "used", that is, you'll
|
||||
// get a warning if you don't handle a `Result` that you get in your
|
||||
// function. Read more about that in the `std::result` module docs:
|
||||
// https://doc.rust-lang.org/std/result/#results-must-be-used
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// This example panics because the second time it calls `pop`, the `vec`
|
||||
// is empty, so `pop` returns `None`, and `unwrap` panics if it's called
|
||||
// on `None`. Handle this in a more graceful way than calling `unwrap`!
|
||||
// Scroll down for hints :)
|
||||
// Execute `rustlings hint option1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn pop_too_much() -> bool {
|
||||
let mut list = vec![3];
|
||||
@@ -27,31 +29,3 @@ mod tests {
|
||||
assert!(pop_too_much());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Try using a `match` statement where the arms are `Some(thing)` and `None`.
|
||||
// Or set a default value to print out if you get `None` by using the
|
||||
// function `unwrap_or`.
|
||||
// Or use an `if let` statement on the result of `pop()` to both destructure
|
||||
// a `Some` value and only print out something if we have a value!
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// result1.rs
|
||||
// Make this test pass! Scroll down for hints :)
|
||||
// Make this test pass! Execute `rustlings hint result1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
@@ -25,22 +27,3 @@ fn test_creation() {
|
||||
);
|
||||
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// `PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result.
|
||||
// It should be doing some checking, returning an `Err` result if those checks fail, and only
|
||||
// returning an `Ok` result if those checks determine that everything is... okay :)
|
||||
|
||||
@@ -1,44 +1,8 @@
|
||||
// functions1.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint functions1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
call_me();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This main function is calling a function that it expects to exist, but the
|
||||
// function doesn't exist. It expects this function to have the name `call_me`.
|
||||
// It expects this function to not take any arguments and not return a value.
|
||||
// Sounds a lot like `main`, doesn't it?
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// functions2.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint functions2` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
call_me(3);
|
||||
@@ -10,33 +12,3 @@ fn call_me(num) {
|
||||
println!("Ring! Call number {}", i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Rust requires that all parts of a function's signature have type annotations,
|
||||
// but `call_me` is missing the type annotation of `num`.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// functions3.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint functions3` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
call_me();
|
||||
@@ -10,33 +12,3 @@ fn call_me(num: i32) {
|
||||
println!("Ring! Call number {}", i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This time, the function *declaration* is okay, but there's something wrong
|
||||
// with the place where we're calling the function.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// functions4.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint functions4` for hints :)
|
||||
|
||||
// This store is having a sale where if the price is an even number, you get
|
||||
// 10 (money unit) off, but if it's an odd number, it's 3 (money unit) less.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let original_price = 51;
|
||||
println!("Your sale price is {}", sale_price(original_price));
|
||||
@@ -20,25 +22,3 @@ fn sale_price(price: i32) -> {
|
||||
fn is_even(num: i32) -> bool {
|
||||
num % 2 == 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The error message points to line 12 and says it expects a type after the
|
||||
// `->`. This is where the function's return type should be-- take a look at
|
||||
// the `is_even` function for an example!
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// functions5.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint functions5` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let answer = square(3);
|
||||
@@ -9,39 +11,3 @@ fn main() {
|
||||
fn square(num: i32) -> i32 {
|
||||
num * num;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This is a really common error that can be fixed by removing one character.
|
||||
// It happens because Rust distinguishes between expressions and statements: expressions return
|
||||
// a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language.
|
||||
// We want to return a value of `i32` type from the `square` function, but it is returning a `()` type...
|
||||
// They are not the same. There are two solutions:
|
||||
// 1. Add a `return` ahead of `num * num;`
|
||||
// 2. remove `;`, make it to be `num * num`
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// if1.rs
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn bigger(a: i32, b: i32) -> i32 {
|
||||
// Complete this function to return the bigger number!
|
||||
// Do not use:
|
||||
// - return
|
||||
// - another function call
|
||||
// - additional variables
|
||||
// Scroll down for hints.
|
||||
// Execute `rustlings hint if1` for hints
|
||||
}
|
||||
|
||||
// Don't mind this for now :)
|
||||
@@ -24,36 +25,3 @@ mod tests {
|
||||
assert_eq!(42, bigger(32, 42));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// It's possible to do this in one line if you would like!
|
||||
// Some similar examples from other languages:
|
||||
// - In C(++) this would be: `a > b ? a : b`
|
||||
// - In Python this would be: `a if a > b else b`
|
||||
// Remember in Rust that:
|
||||
// - the `if` condition does not need to be surrounded by parentheses
|
||||
// - `if`/`else` conditionals are expressions
|
||||
// - Each condition is followed by a `{}` block.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// macros1.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint macros1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
@@ -10,55 +12,3 @@ macro_rules! my_macro {
|
||||
fn main() {
|
||||
my_macro();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// When you call a macro, you need to add something special compared to a
|
||||
// regular function call. If you're stuck, take a look at what's inside
|
||||
// `my_macro`.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// macros2.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint macros2` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
my_macro!();
|
||||
@@ -10,64 +12,3 @@ macro_rules! my_macro {
|
||||
println!("Check out my macro!");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Macros don't quite play by the same rules as the rest of Rust, in terms of
|
||||
// what's available where.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Unlike other things in Rust, the order of "where you define a macro" versus
|
||||
// "where you use it" actually matters.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// macros3.rs
|
||||
// Make me compile, without taking the macro out of the module! Scroll down for hints :)
|
||||
// Make me compile, without taking the macro out of the module!
|
||||
// Execute `rustlings hint macros3` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
mod macros {
|
||||
macro_rules! my_macro {
|
||||
@@ -12,64 +15,3 @@ mod macros {
|
||||
fn main() {
|
||||
my_macro!();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// In order to use a macro outside of its module, you need to do something
|
||||
// special to the module to lift the macro out into its parent.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The same trick also works on "extern crate" statements for crates that have
|
||||
// exported macros, if you've seen any of those around.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// macros4.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint macros4` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
@@ -14,64 +16,3 @@ fn main() {
|
||||
my_macro!();
|
||||
my_macro!(7777);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// You only need to add a single character to make this compile.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The way macros are written, it wants to see something between each
|
||||
// "macro arm", so it can separate them.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// modules1.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint modules1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
mod sausage_factory {
|
||||
fn make_sausage() {
|
||||
@@ -10,34 +12,3 @@ mod sausage_factory {
|
||||
fn main() {
|
||||
sausage_factory::make_sausage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Everything is private in Rust by default-- but there's a keyword we can use
|
||||
// to make something public! The compiler error should point to the thing that
|
||||
// needs to be public.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// modules2.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint modules2` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
mod delicious_snacks {
|
||||
use self::fruits::PEAR as fruit;
|
||||
@@ -23,25 +25,3 @@ fn main() {
|
||||
delicious_snacks::veggie
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The delicious_snacks module is trying to present an external
|
||||
// interface (the `fruit` and `veggie` constants) that is different than
|
||||
// its internal structure (the `fruits` and `veggies` modules and
|
||||
// associated constants). It's almost there except for one keyword missing for
|
||||
// each constant.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// move_semantics1.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute `rustlings hint move_semantics1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let vec0 = Vec::new();
|
||||
@@ -22,21 +24,3 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 11,
|
||||
// right? The fix for this is going to be adding one keyword, and the addition is NOT on line 11
|
||||
// where the error is.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// move_semantics2.rs
|
||||
// Make me compile without changing line 10! Scroll down for hints :)
|
||||
// Make me compile without changing line 10!
|
||||
// Execute `rustlings hint move_semantics2` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let vec0 = Vec::new();
|
||||
@@ -23,31 +26,3 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// So `vec0` is being *moved* into the function `fill_vec` when we call it on
|
||||
// line 7, which means it gets dropped at the end of `fill_vec`, which means we
|
||||
// can't use `vec0` again on line 10 (or anywhere else in `main` after the
|
||||
// `fill_vec` call for that matter). We could fix this in a few ways, try them
|
||||
// all!
|
||||
// 1. Make another, separate version of the data that's in `vec0` and pass that
|
||||
// to `fill_vec` instead.
|
||||
// 2. Make `fill_vec` borrow its argument instead of taking ownership of it,
|
||||
// and then copy the data within the function in order to return an owned
|
||||
// `Vec<i32>`
|
||||
// 3. Make `fill_vec` *mutably* borrow its argument (which will need to be
|
||||
// mutable), modify it directly, then not return anything. Then you can get rid
|
||||
// of `vec1` entirely -- note that this will change what gets printed by the
|
||||
// first `println!`
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// move_semantics3.rs
|
||||
// Make me compile without adding new lines-- just changing existing lines!
|
||||
// (no lines with multiple semicolons necessary!)
|
||||
// Scroll down for hints :)
|
||||
// Execute `rustlings hint move_semantics3` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let vec0 = Vec::new();
|
||||
@@ -22,24 +24,3 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The difference between this one and the previous ones is that the first line
|
||||
// of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can,
|
||||
// instead of adding that line back, add `mut` in one place that will change
|
||||
// an existing binding to be a mutable binding instead of an immutable one :)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// move_semantics4.rs
|
||||
// Refactor this code so that instead of having `vec0` and creating the vector
|
||||
// in `fn main`, we instead create it within `fn fill_vec` and transfer the
|
||||
// freshly created vector from fill_vec to its caller. Scroll for hints!
|
||||
// freshly created vector from fill_vec to its caller.
|
||||
// Execute `rustlings hint move_semantics4` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let vec0 = Vec::new();
|
||||
@@ -25,24 +28,3 @@ fn fill_vec() -> Vec<i32> {
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Stop reading whenever you feel like you have enough direction :) Or try
|
||||
// doing one step and then fixing the compiler errors that result!
|
||||
// So the end goal is to:
|
||||
// - get rid of the first line in main that creates the new vector
|
||||
// - so then `vec0` doesn't exist, so we can't pass it to `fill_vec`
|
||||
// - we don't want to pass anything to `fill_vec`, so its signature should
|
||||
// reflect that it does not take any arguments
|
||||
// - since we're not creating a new vec in `main` anymore, we need to create
|
||||
// a new vec in `fill_vec`, similarly to the way we did in `main`
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Fill in the rest of the line that has code missing!
|
||||
// No hints, there's no tricks, just get used to typing these :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
// Booleans (`bool`)
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Fill in the rest of the line that has code missing!
|
||||
// No hints, there's no tricks, just get used to typing these :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
// Characters (`char`)
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// primitive_types3.rs
|
||||
// Create an array with at least 100 elements in it where the ??? is.
|
||||
// Scroll down for hints!
|
||||
// Execute `rustlings hint primitive_types3` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let a = ???
|
||||
@@ -11,37 +13,3 @@ fn main() {
|
||||
println!("Meh, I eat arrays like that for breakfast.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// There's a shorthand to initialize Arrays with a certain size that does not
|
||||
// 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];
|
||||
|
||||
// Bonus: what are some other things you could have that would return true
|
||||
// for `a.len() >= 100`?
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// primitive_types4.rs
|
||||
// Get a slice out of Array a where the ??? is so that the `if` statement
|
||||
// returns true. Scroll down for hints!!
|
||||
// returns true. Execute `rustlings hint primitive_types4` for hints!!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[test]
|
||||
fn main() {
|
||||
@@ -10,59 +12,3 @@ fn main() {
|
||||
|
||||
assert_eq!([2, 3, 4], nice_slice)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book:
|
||||
// https://doc.rust-lang.org/book/ch04-03-slices.html
|
||||
// and use the starting and ending indices of the items in the Array
|
||||
// that you want to end up in the slice.
|
||||
|
||||
// If you're curious why the right hand of the `==` comparison does not
|
||||
// have an ampersand for a reference since the left hand side is a
|
||||
// reference, take a look at the Deref coercions section of the book:
|
||||
// https://doc.rust-lang.org/book/ch15-02-deref.html
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// primitive_types5.rs
|
||||
// Destructure the `cat` tuple so that the println will work.
|
||||
// Scroll down for hints!
|
||||
// Execute `rustlings hint primitive_types5` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let cat = ("Furry McFurson", 3.5);
|
||||
@@ -8,38 +10,3 @@ fn main() {
|
||||
|
||||
println!("{} is {} years old.", name, age);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Take a look at the Data Types -> The Tuple Type section of the book:
|
||||
// https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
||||
// Particularly the part about destructuring (second to last example in the section).
|
||||
// You'll need to make a pattern to bind `name` and `age` to the appropriate parts
|
||||
// of the tuple. You can do it!!
|
||||
|
||||
@@ -1,45 +1,11 @@
|
||||
// primitive_types6.rs
|
||||
// Use a tuple index to access the second element of `numbers`.
|
||||
// You can put this right into the `println!` where the ??? is.
|
||||
// Scroll down for hints!
|
||||
// Execute `rustlings hint primitive_types6` for hints!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let numbers = (1, 2, 3);
|
||||
println!("The second number is {}", ???);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// While you could use a destructuring `let` for the tuple here, try
|
||||
// indexing into it instead, as explained in the last example of the
|
||||
// Data Types -> The Tuple Type section of the book:
|
||||
// https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
||||
// Now you have another tool in your toolbox!
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Make this code compile by filling in a value for `shared_numbers` where the
|
||||
// TODO comment is and creating an initial binding for `child_numbers`
|
||||
// somewhere. Try not to create any copies of the `numbers` Vec!
|
||||
// Scroll down for hints :)
|
||||
// Execute `rustlings help arc1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
@@ -27,29 +29,3 @@ fn main() {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order
|
||||
// to avoid creating a copy of `numbers`, you'll need to create `child_numbers`
|
||||
// inside the loop but still in the main thread.
|
||||
|
||||
// `child_numbers` should be a clone of the Arc of the numbers instead of a
|
||||
// thread-local copy of the numbers.
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// Step 1. Complete the `capitalize_first` function to pass the first two cases
|
||||
// Step 2. Apply the `capitalize_first` function to a vector of strings, ensuring that it returns a vector of strings as well
|
||||
// Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string
|
||||
// As always, there are hints below!
|
||||
// As always, there are hints if you execute `rustlings hint iterators2`!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn capitalize_first(input: &str) -> String {
|
||||
let mut c = input.chars();
|
||||
@@ -44,102 +46,3 @@ mod tests {
|
||||
assert_eq!(capitalized_words, "Hello World");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Step 1
|
||||
// You need to call something on `first` before it can be collected
|
||||
// Currently its type is `char`. Have a look at the methods that are available on that type:
|
||||
// https://doc.rust-lang.org/std/primitive.char.html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Step 2
|
||||
// First you'll need to turn the Vec into an iterator
|
||||
// Then you'll need to apply your function unto each item in the vector
|
||||
// P.s. Don't forget to collect() at the end!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Step 3.
|
||||
// This is very similar to the previous test. The only real change is that you will need to
|
||||
// alter the type that collect is coerced into. For a bonus you could try doing this with a
|
||||
// turbofish
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
// 1. Complete the divide function to get the first four tests to pass
|
||||
// 2. Uncomment the last two tests and get them to pass by filling in
|
||||
// values for `x` using `division_results`.
|
||||
// Scroll down for a minor hint for part 2, and scroll down further for
|
||||
// a major hint.
|
||||
// Execute `rustlings hint iterators3` to get some hints!
|
||||
// Have fun :-)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DivisionError {
|
||||
NotDivisible(NotDivisibleError),
|
||||
@@ -75,72 +76,3 @@ mod tests {
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Minor hint: In each of the two cases in the match in main, you can create x with either
|
||||
// a 'turbofish' or by hinting the type of x to the compiler. You may try both.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Major hint: Have a look at the Iter trait and at the explanation of its collect function.
|
||||
// Especially the part about Result is interesting.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// iterators4.rs
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn factorial(num: u64) -> u64 {
|
||||
// Complete this function to return factorial of num
|
||||
// Do not use:
|
||||
@@ -9,7 +11,7 @@ pub fn factorial(num: u64) -> u64 {
|
||||
// - additional variables
|
||||
// For the most fun don't use:
|
||||
// - recursion
|
||||
// Scroll down for hints.
|
||||
// Execute `rustlings hint iterators4` for hints.
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -30,32 +32,3 @@ mod tests {
|
||||
assert_eq!(24, factorial(4));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// In an imperative language you might write a for loop to iterate through
|
||||
// multiply the values into a mutable variable. Or you might write code more
|
||||
// functionally with recursion and a match clause. But you can also use ranges
|
||||
// and iterators to solve this in rust.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// strings1.rs
|
||||
// Make me compile without changing the function signature! Scroll down for hints :)
|
||||
// Make me compile without changing the function signature!
|
||||
// Execute `rustlings hint strings1` for hints ;)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let answer = current_favorite_color();
|
||||
@@ -9,38 +12,3 @@ fn main() {
|
||||
fn current_favorite_color() -> String {
|
||||
"blue"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The `current_favorite_color` function is currently returning a string slice with the `'static`
|
||||
// lifetime. We know this because the data of the string lives in our code itself -- it doesn't
|
||||
// come from a file or user input or another program -- so it will live as long as our program
|
||||
// lives. But it is still a string slice. There's one way to create a `String` by converting a
|
||||
// string slice covered in the Strings chapter of the book, and another way that uses the `From`
|
||||
// trait.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// strings2.rs
|
||||
// Make me compile without changing the function signature! Scroll down for hints :)
|
||||
// Make me compile without changing the function signature!
|
||||
// Execute `rustlings hint strings2` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let word = String::from("green"); // Try not changing this line :)
|
||||
@@ -13,32 +16,3 @@ fn main() {
|
||||
fn is_a_color_word(attempt: &str) -> bool {
|
||||
attempt == "green" || attempt == "blue" || attempt == "red"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Yes, it would be really easy to fix this by just changing the value bound to `word` to be a
|
||||
// string slice instead of a `String`, wouldn't it?? There is a way to add one character to line
|
||||
// 6, though, that will coerce the `String` into a string slice.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// structs1.rs
|
||||
// Address all the TODOs to make the tests pass!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
struct ColorClassicStruct {
|
||||
// TODO: Something goes here
|
||||
}
|
||||
@@ -26,7 +28,6 @@ mod tests {
|
||||
#[test]
|
||||
fn tuple_structs() {
|
||||
// TODO: Instantiate a tuple struct!
|
||||
// For more fun, use the field initialization shorthand.
|
||||
// let green =
|
||||
|
||||
assert_eq!(green.0, "green");
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Address all the TODOs to make the tests pass!
|
||||
// No hints, just do it!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Order {
|
||||
name: String,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
// more than 40 at once, each apple only costs 1! Write a function that calculates
|
||||
// the price of an order of apples given the order amount. No hints this time!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
// Put your function here!
|
||||
// fn ..... {
|
||||
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
// test2.rs
|
||||
// This is a test for the following sections:
|
||||
// - Tests
|
||||
// - Strings
|
||||
|
||||
// This test isn't testing our function -- make it do that in such a way that
|
||||
// the test passes. Then write a second test that tests that we get the result
|
||||
// we expect to get when we call `times_two` with a negative number.
|
||||
// No hints, you can do this :)
|
||||
// Ok, here are a bunch of values-- some are `Strings`, some are `&strs`. Your
|
||||
// task is to call one of these two functions on each value depending on what
|
||||
// you think each value is. That is, add either `string_slice` or `string`
|
||||
// before the parentheses on each line. If you're right, it will compile!
|
||||
|
||||
pub fn times_two(num: i32) -> i32 {
|
||||
num * 2
|
||||
// I AM NOT DONE
|
||||
|
||||
fn string_slice(arg: &str) {
|
||||
println!("{}", arg);
|
||||
}
|
||||
fn string(arg: String) {
|
||||
println!("{}", arg);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn returns_twice_of_positive_numbers() {
|
||||
assert_eq!(times_two(4), ???);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_twice_of_negative_numbers() {
|
||||
// TODO write an assert for `times_two(-4)`
|
||||
}
|
||||
fn main() {
|
||||
("blue");
|
||||
("red".to_string());
|
||||
(String::from("hi"));
|
||||
("rust is fun!".to_owned());
|
||||
("nice weather".into());
|
||||
(format!("Interpolation {}", "Station"));
|
||||
(&String::from("abc")[0..1]);
|
||||
(" hello there ".trim());
|
||||
("Happy Monday!".to_string().replace("Mon", "Tues"));
|
||||
("mY sHiFt KeY iS sTiCkY".to_lowercase());
|
||||
}
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
// strings3.rs
|
||||
// test3.rs
|
||||
// This is a test for the following sections:
|
||||
// - Strings
|
||||
// - Tests
|
||||
|
||||
// Ok, here are a bunch of values-- some are `Strings`, some are `&strs`. Your
|
||||
// task is to call one of these two functions on each value depending on what
|
||||
// you think each value is. That is, add either `string_slice` or `string`
|
||||
// before the parentheses on each line. If you're right, it will compile!
|
||||
// This test isn't testing our function -- make it do that in such a way that
|
||||
// the test passes. Then write a second test that tests that we get the result
|
||||
// we expect to get when we call `times_two` with a negative number.
|
||||
// No hints, you can do this :)
|
||||
|
||||
fn string_slice(arg: &str) {
|
||||
println!("{}", arg);
|
||||
}
|
||||
fn string(arg: String) {
|
||||
println!("{}", arg);
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn times_two(num: i32) -> i32 {
|
||||
num * 2
|
||||
}
|
||||
|
||||
fn main() {
|
||||
("blue");
|
||||
("red".to_string());
|
||||
(String::from("hi"));
|
||||
("rust is fun!".to_owned());
|
||||
("nice weather".into());
|
||||
(format!("Interpolation {}", "Station"));
|
||||
(&String::from("abc")[0..1]);
|
||||
(" hello there ".trim());
|
||||
("Happy Monday!".to_string().replace("Mon", "Tues"));
|
||||
("mY sHiFt KeY iS sTiCkY".to_lowercase());
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn returns_twice_of_positive_numbers() {
|
||||
assert_eq!(times_two(4), ???);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_twice_of_negative_numbers() {
|
||||
// TODO write an assert for `times_two(-4)`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
// Write a macro that passes the test! No hints this time, you can do it!
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
if my_macro!("world!") != "Hello world!" {
|
||||
panic!("Oh no! Wrong output!");
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// rustlings run --test exercises/tests/tests1.rs
|
||||
|
||||
// This test has a problem with it -- make the test compile! Make the test
|
||||
// pass! Make the test fail! Scroll down for hints :)
|
||||
// pass! Make the test fail! Execute `rustlings hint tests1` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -13,37 +15,3 @@ mod tests {
|
||||
assert!();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// You don't even need to write any code to test -- you can just test values and run that, even
|
||||
// though you wouldn't do that in real life :) `assert!` is a macro that needs an argument.
|
||||
// Depending on the value of the argument, `assert!` will do nothing (in which case the test will
|
||||
// pass) or `assert!` will panic (in which case the test will fail). So try giving different values
|
||||
// to `assert!` and see which ones compile, which ones pass, and which ones fail :)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// tests2.rs
|
||||
// This test has a problem with it -- make the test compile! Make the test
|
||||
// pass! Make the test fail! Scroll down for hints :)
|
||||
// pass! Make the test fail! Execute `rustlings hint tests2` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -9,36 +11,3 @@ mod tests {
|
||||
assert_eq!();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Like the previous exercise, you don't need to write any code to get this test to compile and
|
||||
// run. `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two
|
||||
// values that are equal! Try giving it two arguments that are different! Try giving it two values
|
||||
// that are of different types! Try switching which argument comes first and which comes second!
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// tests3.rs
|
||||
// This test isn't testing our function -- make it do that in such a way that
|
||||
// the test passes. Then write a second test that tests whether we get the result
|
||||
// we expect to get when we call `is_even(5)`. Scroll down for hints!
|
||||
// we expect to get when we call `is_even(5)`.
|
||||
// Execute `rustlings hint tests3` for hints :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn is_even(num: i32) -> bool {
|
||||
num % 2 == 0
|
||||
@@ -16,28 +19,3 @@ mod tests {
|
||||
assert!();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// You can call a function right where you're passing arguments to `assert!` -- so you could do
|
||||
// something like `assert!(having_fun())`. If you want to check that you indeed get false, you
|
||||
// can negate the result of what you're doing using `!`, like `assert!(!having_fun())`.
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// threads1.rs
|
||||
// Make this compile! Scroll down for hints :) The idea is the thread
|
||||
// spawned on line 19 is completing jobs while the main thread is
|
||||
// Make this compile! Execute `rustlings hint threads1` for hints :)
|
||||
// The idea is the thread spawned on line 19 is completing jobs while the main thread is
|
||||
// monitoring progress until 10 jobs are completed. If you see 6 lines
|
||||
// of "waiting..." and the program ends without timing out the playground,
|
||||
// of "waiting..." and the program ends without timing out when running,
|
||||
// you've got it :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -27,69 +29,3 @@ fn main() {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// `Arc` is an Atomic Reference Counted pointer that allows safe, shared access
|
||||
// to **immutable** data. But we want to *change* the number of `jobs_completed`
|
||||
// so we'll need to also use another type that will only allow one thread to
|
||||
// mutate the data at a time. Take a look at this section of the book:
|
||||
// https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct
|
||||
// and keep scrolling if you'd like more hints :)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like:
|
||||
// `let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));`
|
||||
// Similar to the code in the example in the book that happens after the text
|
||||
// that says "We can use Arc<T> to fix this.". If not, give that a try! If you
|
||||
// do and would like more hints, keep scrolling!!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Make sure neither of your threads are holding onto the lock of the mutex
|
||||
// while they are sleeping, since this will prevent the other thread from
|
||||
// being allowed to get the lock. Locks are automatically released when
|
||||
// they go out of scope.
|
||||
|
||||
// Ok, so, real talk, this was actually tricky for *me* to do too. And
|
||||
// I could see a lot of different problems you might run into, so at this
|
||||
// point I'm not sure which one you've hit :) Please see a few possible
|
||||
// answers on https://github.com/carols10cents/rustlings/issues/3 --
|
||||
// mine is a little more complicated because I decided I wanted to see
|
||||
// the number of jobs currently done when I was checking the status.
|
||||
|
||||
// Please open an issue if you're still running into a problem that
|
||||
// these hints are not helping you with, or if you've looked at the sample
|
||||
// answers and don't understand why they work and yours doesn't.
|
||||
|
||||
// If you've learned from the sample solutions, I encourage you to come
|
||||
// back to this exercise and try it again in a few days to reinforce
|
||||
// what you've learned :)
|
||||
|
||||
@@ -1,42 +1,14 @@
|
||||
// variables1.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :)
|
||||
|
||||
// About this `I AM NOT DONE` thing:
|
||||
// We sometimes encourage you to keep trying things on a given exercise,
|
||||
// even after you already figured it out. If you got everything working and
|
||||
// feel ready for the next exercise, you the `I AM NOT DONE` comment below.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
x = 5;
|
||||
println!("x has the value {}", x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Hint: The declaration on line 5 is missing a keyword that is needed in Rust
|
||||
// to create a new variable binding.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// variables2.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let x;
|
||||
@@ -9,39 +11,3 @@ fn main() {
|
||||
println!("Not ten!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// The compiler message is saying that Rust cannot infer the type that the
|
||||
// variable binding `x` has with what is given here.
|
||||
// What happens if you annotate line 5 with a type annotation?
|
||||
// What if you give x a value?
|
||||
// What if you do both?
|
||||
// What type should x be, anyway?
|
||||
// What if x is the same type as 10? What if it's a different type?
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// variables3.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let x = 3;
|
||||
@@ -7,37 +9,3 @@ fn main() {
|
||||
x = 5;
|
||||
println!("Number {}", x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// In Rust, variable bindings are immutable by default. But here we're trying
|
||||
// to reassign a different value to x! There's a keyword we can use to make
|
||||
// a variable binding mutable instead.
|
||||
|
||||
@@ -1,45 +1,9 @@
|
||||
// variables4.rs
|
||||
// Make me compile! Scroll down for hints :)
|
||||
// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :)
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
let x: i32;
|
||||
println!("Number {}", x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Oops! In this exercise, we have a variable binding that we've created on
|
||||
// line 5, and we're trying to use it on line 6, but we haven't given it a
|
||||
// value. We can't print out something that isn't there; try giving x a value!
|
||||
// This is an error that can cause bugs that's very easy to make in any
|
||||
// programming language -- thankfully the Rust compiler has caught this for us!
|
||||
|
||||
455
info.toml
455
info.toml
@@ -1,241 +1,612 @@
|
||||
# VARIABLES
|
||||
|
||||
[[exercises]]
|
||||
name = "variables1"
|
||||
path = "exercises/variables/variables1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Hint: The declaration on line 5 is missing a keyword that is needed in Rust
|
||||
to create a new variable binding."""
|
||||
|
||||
[[exercises]]
|
||||
name = "variables2"
|
||||
path = "exercises/variables/variables2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
The compiler message is saying that Rust cannot infer the type that the
|
||||
variable binding `x` has with what is given here.
|
||||
What happens if you annotate line 5 with a type annotation?
|
||||
What if you give x a value?
|
||||
What if you do both?
|
||||
What type should x be, anyway?
|
||||
What if x is the same type as 10? What if it's a different type?"""
|
||||
|
||||
[[exercises]]
|
||||
name = "variables3"
|
||||
path = "exercises/variables/variables3.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
In Rust, variable bindings are immutable by default. But here we're trying
|
||||
to reassign a different value to x! There's a keyword we can use to make
|
||||
a variable binding mutable instead."""
|
||||
|
||||
[[exercises]]
|
||||
name = "variables4"
|
||||
path = "exercises/variables/variables4.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Oops! In this exercise, we have a variable binding that we've created on
|
||||
line 5, and we're trying to use it on line 6, but we haven't given it a
|
||||
value. We can't print out something that isn't there; try giving x a value!
|
||||
This is an error that can cause bugs that's very easy to make in any
|
||||
programming language -- thankfully the Rust compiler has caught this for us!"""
|
||||
|
||||
# IF
|
||||
|
||||
[[exercises]]
|
||||
name = "if1"
|
||||
path = "exercises/if/if1.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
It's possible to do this in one line if you would like!
|
||||
Some similar examples from other languages:
|
||||
- In C(++) this would be: `a > b ? a : b`
|
||||
- In Python this would be: `a if a > b else b`
|
||||
Remember in Rust that:
|
||||
- the `if` condition does not need to be surrounded by parentheses
|
||||
- `if`/`else` conditionals are expressions
|
||||
- Each condition is followed by a `{}` block."""
|
||||
|
||||
# FUNCTIONS
|
||||
|
||||
[[exercises]]
|
||||
name = "functions1"
|
||||
path = "exercises/functions/functions1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
This main function is calling a function that it expects to exist, but the
|
||||
function doesn't exist. It expects this function to have the name `call_me`.
|
||||
It expects this function to not take any arguments and not return a value.
|
||||
Sounds a lot like `main`, doesn't it?"""
|
||||
|
||||
[[exercises]]
|
||||
name = "functions2"
|
||||
path = "exercises/functions/functions2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Rust requires that all parts of a function's signature have type annotations,
|
||||
but `call_me` is missing the type annotation of `num`."""
|
||||
|
||||
[[exercises]]
|
||||
name = "functions3"
|
||||
path = "exercises/functions/functions3.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
This time, the function *declaration* is okay, but there's something wrong
|
||||
with the place where we're calling the function."""
|
||||
|
||||
[[exercises]]
|
||||
name = "functions4"
|
||||
path = "exercises/functions/functions4.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
The error message points to line 12 and says it expects a type after the
|
||||
`->`. This is where the function's return type should be-- take a look at
|
||||
the `is_even` function for an example!"""
|
||||
|
||||
[[exercises]]
|
||||
name = "functions5"
|
||||
path = "exercises/functions/functions5.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
This is a really common error that can be fixed by removing one character.
|
||||
It happens because Rust distinguishes between expressions and statements: expressions return
|
||||
a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language.
|
||||
We want to return a value of `i32` type from the `square` function, but it is returning a `()` type...
|
||||
They are not the same. There are two solutions:
|
||||
1. Add a `return` ahead of `num * num;`
|
||||
2. remove `;`, make it to be `num * num`"""
|
||||
|
||||
# TEST 1
|
||||
|
||||
[[exercises]]
|
||||
name = "test1"
|
||||
path = "exercises/test1.rs"
|
||||
mode = "test"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
# PRIMITIVE TYPES
|
||||
|
||||
[[exercises]]
|
||||
name = "primitive_types1"
|
||||
path = "exercises/primitive_types/primitive_types1.rs"
|
||||
mode = "compile"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
[[exercises]]
|
||||
name = "primitive_types2"
|
||||
path = "exercises/primitive_types/primitive_types2.rs"
|
||||
mode = "compile"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
[[exercises]]
|
||||
name = "primitive_types3"
|
||||
path = "exercises/primitive_types/primitive_types3.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
There's a shorthand to initialize Arrays with a certain size that does not
|
||||
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];
|
||||
|
||||
Bonus: what are some other things you could have that would return true
|
||||
for `a.len() >= 100`?"""
|
||||
|
||||
[[exercises]]
|
||||
name = "primitive_types4"
|
||||
path = "exercises/primitive_types/primitive_types4.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book:
|
||||
https://doc.rust-lang.org/book/ch04-03-slices.html
|
||||
and use the starting and ending indices of the items in the Array
|
||||
that you want to end up in the slice.
|
||||
|
||||
If you're curious why the right hand of the `==` comparison does not
|
||||
have an ampersand for a reference since the left hand side is a
|
||||
reference, take a look at the Deref coercions section of the book:
|
||||
https://doc.rust-lang.org/book/ch15-02-deref.html"""
|
||||
|
||||
[[exercises]]
|
||||
name = "primitive_types5"
|
||||
path = "exercises/primitive_types/primitive_types5.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Take a look at the Data Types -> The Tuple Type section of the book:
|
||||
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
||||
Particularly the part about destructuring (second to last example in the section).
|
||||
You'll need to make a pattern to bind `name` and `age` to the appropriate parts
|
||||
of the tuple. You can do it!!"""
|
||||
|
||||
[[exercises]]
|
||||
name = "primitive_types6"
|
||||
path = "exercises/primitive_types/primitive_types6.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
While you could use a destructuring `let` for the tuple here, try
|
||||
indexing into it instead, as explained in the last example of the
|
||||
Data Types -> The Tuple Type section of the book:
|
||||
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
||||
Now you have another tool in your toolbox!"""
|
||||
|
||||
# STRUCTS
|
||||
|
||||
[[exercises]]
|
||||
name = "structs1"
|
||||
path = "exercises/structs/structs1.rs"
|
||||
mode = "test"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
[[exercises]]
|
||||
name = "structs2"
|
||||
path = "exercises/structs/structs2.rs"
|
||||
mode = "test"
|
||||
|
||||
# ENUMS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/enums/enums1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/enums/enums2.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/enums/enums3.rs"
|
||||
mode = "test"
|
||||
|
||||
# TESTS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/tests/tests1.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/tests/tests2.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/tests/tests3.rs"
|
||||
mode = "test"
|
||||
|
||||
# TEST 2
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/test2.rs"
|
||||
mode = "test"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
# STRINGS
|
||||
|
||||
[[exercises]]
|
||||
name = "strings1"
|
||||
path = "exercises/strings/strings1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
The `current_favorite_color` function is currently returning a string slice with the `'static`
|
||||
lifetime. We know this because the data of the string lives in our code itself -- it doesn't
|
||||
come from a file or user input or another program -- so it will live as long as our program
|
||||
lives. But it is still a string slice. There's one way to create a `String` by converting a
|
||||
string slice covered in the Strings chapter of the book, and another way that uses the `From`
|
||||
trait."""
|
||||
|
||||
[[exercises]]
|
||||
name = "strings2"
|
||||
path = "exercises/strings/strings2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Yes, it would be really easy to fix this by just changing the value bound to `word` to be a
|
||||
string slice instead of a `String`, wouldn't it?? There is a way to add one character to line
|
||||
6, though, that will coerce the `String` into a string slice."""
|
||||
|
||||
# TEST 2
|
||||
|
||||
[[exercises]]
|
||||
name = "test2"
|
||||
path = "exercises/test2.rs"
|
||||
mode = "compile"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
# ENUMS
|
||||
|
||||
[[exercises]]
|
||||
name = "enums1"
|
||||
path = "exercises/enums/enums1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Hint: The declaration of the enumeration type has not been defined yet."""
|
||||
|
||||
[[exercises]]
|
||||
name = "enums2"
|
||||
path = "exercises/enums/enums2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Hint: you can create enumerations that have different variants with different types
|
||||
such as no data, anonymous structs, a single string, tuples, ...etc"""
|
||||
|
||||
[[exercises]]
|
||||
name = "enums3"
|
||||
path = "exercises/enums/enums3.rs"
|
||||
mode = "test"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
# TESTS
|
||||
|
||||
[[exercises]]
|
||||
name = "tests1"
|
||||
path = "exercises/tests/tests1.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
You don't even need to write any code to test -- you can just test values and run that, even
|
||||
though you wouldn't do that in real life :) `assert!` is a macro that needs an argument.
|
||||
Depending on the value of the argument, `assert!` will do nothing (in which case the test will
|
||||
pass) or `assert!` will panic (in which case the test will fail). So try giving different values
|
||||
to `assert!` and see which ones compile, which ones pass, and which ones fail :)"""
|
||||
|
||||
[[exercises]]
|
||||
name = "tests2"
|
||||
path = "exercises/tests/tests2.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
Like the previous exercise, you don't need to write any code to get this test to compile and
|
||||
run. `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two
|
||||
values that are equal! Try giving it two arguments that are different! Try giving it two values
|
||||
that are of different types! Try switching which argument comes first and which comes second!"""
|
||||
|
||||
[[exercises]]
|
||||
name = "tests3"
|
||||
path = "exercises/tests/tests3.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
You can call a function right where you're passing arguments to `assert!` -- so you could do
|
||||
something like `assert!(having_fun())`. If you want to check that you indeed get false, you
|
||||
can negate the result of what you're doing using `!`, like `assert!(!having_fun())`."""
|
||||
|
||||
# TEST 3
|
||||
|
||||
[[exercises]]
|
||||
name = "test3"
|
||||
path = "exercises/test3.rs"
|
||||
mode = "compile"
|
||||
mode = "test"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
# MODULES
|
||||
|
||||
[[exercises]]
|
||||
name = "modules1"
|
||||
path = "exercises/modules/modules1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Everything is private in Rust by default-- but there's a keyword we can use
|
||||
to make something public! The compiler error should point to the thing that
|
||||
needs to be public."""
|
||||
|
||||
[[exercises]]
|
||||
name = "modules2"
|
||||
path = "exercises/modules/modules2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
The delicious_snacks module is trying to present an external
|
||||
interface (the `fruit` and `veggie` constants) that is different than
|
||||
its internal structure (the `fruits` and `veggies` modules and
|
||||
associated constants). It's almost there except for one keyword missing for
|
||||
each constant."""
|
||||
|
||||
# MACROS
|
||||
|
||||
[[exercises]]
|
||||
name = "macros1"
|
||||
path = "exercises/macros/macros1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
When you call a macro, you need to add something special compared to a
|
||||
regular function call. If you're stuck, take a look at what's inside
|
||||
`my_macro`."""
|
||||
|
||||
[[exercises]]
|
||||
name = "macros2"
|
||||
path = "exercises/macros/macros2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Macros don't quite play by the same rules as the rest of Rust, in terms of
|
||||
what's available where.
|
||||
|
||||
Unlike other things in Rust, the order of "where you define a macro" versus
|
||||
"where you use it" actually matters."""
|
||||
|
||||
[[exercises]]
|
||||
name = "macros3"
|
||||
path = "exercises/macros/macros3.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
In order to use a macro outside of its module, you need to do something
|
||||
special to the module to lift the macro out into its parent.
|
||||
|
||||
The same trick also works on "extern crate" statements for crates that have
|
||||
exported macros, if you've seen any of those around."""
|
||||
|
||||
[[exercises]]
|
||||
name = "macros4"
|
||||
path = "exercises/macros/macros4.rs"
|
||||
mode = "compile"
|
||||
|
||||
hint = """
|
||||
You only need to add a single character to make this compile.
|
||||
The way macros are written, it wants to see something between each
|
||||
"macro arm", so it can separate them."""
|
||||
# TEST 4
|
||||
|
||||
[[exercises]]
|
||||
name = "test4"
|
||||
path = "exercises/test4.rs"
|
||||
mode = "compile"
|
||||
hint = "No hints this time ;)"
|
||||
|
||||
# MOVE SEMANTICS
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics1"
|
||||
path = "exercises/move_semantics/move_semantics1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 11,
|
||||
right? The fix for this is going to be adding one keyword, and the addition is NOT on line 11
|
||||
where the error is."""
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics2"
|
||||
path = "exercises/move_semantics/move_semantics2.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
So `vec0` is being *moved* into the function `fill_vec` when we call it on
|
||||
line 7, which means it gets dropped at the end of `fill_vec`, which means we
|
||||
can't use `vec0` again on line 10 (or anywhere else in `main` after the
|
||||
`fill_vec` call for that matter). We could fix this in a few ways, try them
|
||||
all!
|
||||
1. Make another, separate version of the data that's in `vec0` and pass that
|
||||
to `fill_vec` instead.
|
||||
2. Make `fill_vec` borrow its argument instead of taking ownership of it,
|
||||
and then copy the data within the function in order to return an owned
|
||||
`Vec<i32>`
|
||||
3. Make `fill_vec` *mutably* borrow its argument (which will need to be
|
||||
mutable), modify it directly, then not return anything. Then you can get rid
|
||||
of `vec1` entirely -- note that this will change what gets printed by the
|
||||
first `println!`"""
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics3"
|
||||
path = "exercises/move_semantics/move_semantics3.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
The difference between this one and the previous ones is that the first line
|
||||
of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can,
|
||||
instead of adding that line back, add `mut` in one place that will change
|
||||
an existing binding to be a mutable binding instead of an immutable one :)"""
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics4"
|
||||
path = "exercises/move_semantics/move_semantics4.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Stop reading whenever you feel like you have enough direction :) Or try
|
||||
doing one step and then fixing the compiler errors that result!
|
||||
So the end goal is to:
|
||||
- get rid of the first line in main that creates the new vector
|
||||
- so then `vec0` doesn't exist, so we can't pass it to `fill_vec`
|
||||
- we don't want to pass anything to `fill_vec`, so its signature should
|
||||
reflect that it does not take any arguments
|
||||
- since we're not creating a new vec in `main` anymore, we need to create
|
||||
a new vec in `fill_vec`, similarly to the way we did in `main`"""
|
||||
|
||||
# ERROR HANDLING
|
||||
|
||||
[[exercises]]
|
||||
name = "errors1"
|
||||
path = "exercises/error_handling/errors1.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
`Err` is one of the variants of `Result`, so what the 2nd test is saying
|
||||
is that `generate_nametag_text` should return a `Result` instead of an
|
||||
`Option`.
|
||||
|
||||
To make this change, you'll need to:
|
||||
- update the return type in the function signature to be a Result<String, String> that
|
||||
could be the variants `Ok(String)` and `Err(String)`
|
||||
- change the body of the function to return `Ok(stuff)` where it currently
|
||||
returns `Some(stuff)`
|
||||
- change the body of the function to return `Err(error message)` where it
|
||||
currently returns `None`
|
||||
- change the first test to expect `Ok(stuff)` where it currently expects
|
||||
`Some(stuff)`."""
|
||||
|
||||
[[exercises]]
|
||||
name = "errors2"
|
||||
path = "exercises/error_handling/errors2.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
One way to handle this is using a `match` statement on
|
||||
`item_quantity.parse::<i32>()` where the cases are `Ok(something)` and
|
||||
`Err(something)`. This pattern is very common in Rust, though, so there's
|
||||
a `?` operator that does pretty much what you would make that match statement
|
||||
do for you! Take a look at this section of the Error Handling chapter:
|
||||
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
|
||||
and give it a try!"""
|
||||
|
||||
[[exercises]]
|
||||
name = "errors3"
|
||||
path = "exercises/error_handling/errors3.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
If other functions can return a `Result`, why shouldn't `main`?"""
|
||||
|
||||
[[exercises]]
|
||||
name = "errorsn"
|
||||
path = "exercises/error_handling/errorsn.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
First hint: To figure out what type should go where the ??? is, take a look
|
||||
at the test helper function `test_with_str`, since it returns whatever
|
||||
`read_and_validate` returns and`test_with_str` has its signature fully
|
||||
specified.
|
||||
|
||||
|
||||
Next hint: There are three places in `read_and_validate` that we call a
|
||||
function that returns a `Result` (that is, the functions might fail).
|
||||
Apply the `?` operator on those calls so that we return immediately from
|
||||
`read_and_validate` if those function calls fail.
|
||||
|
||||
|
||||
Another hint: under the hood, the `?` operator calls `From::from`
|
||||
on the error value to convert it to a boxed trait object, a Box<dyn error::Error>,
|
||||
which is polymorphic-- that means that lots of different kinds of errors
|
||||
can be returned from the same function because all errors act the same
|
||||
since they all implement the `error::Error` trait.
|
||||
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
|
||||
|
||||
|
||||
Another another hint: Note that because the `?` operator returns
|
||||
the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
|
||||
`read_and_validate` for *its* success case, we'll have to rewrap a value
|
||||
that we got from the return value of a `?`ed call in an `Ok`-- this will
|
||||
look like `Ok(something)`.
|
||||
|
||||
|
||||
Another another another hint: `Result`s must be "used", that is, you'll
|
||||
get a warning if you don't handle a `Result` that you get in your
|
||||
function. Read more about that in the `std::result` module docs:
|
||||
https://doc.rust-lang.org/std/result/#results-must-be-used"""
|
||||
|
||||
# OPTIONS / RESULTS
|
||||
|
||||
[[exercises]]
|
||||
name = "option1"
|
||||
path = "exercises/error_handling/option1.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
Try using a `match` statement where the arms are `Some(thing)` and `None`.
|
||||
Or set a default value to print out if you get `None` by using the
|
||||
function `unwrap_or`.
|
||||
Or use an `if let` statement on the result of `pop()` to both destructure
|
||||
a `Some` value and only print out something if we have a value!"""
|
||||
|
||||
[[exercises]]
|
||||
name = "result1"
|
||||
path = "exercises/error_handling/result1.rs"
|
||||
mode = "test"
|
||||
|
||||
# THREADS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/threads/threads1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result.
|
||||
It should be doing some checking, returning an `Err` result if those checks fail, and only
|
||||
returning an `Ok` result if those checks determine that everything is... okay :)"""
|
||||
|
||||
# STANDARD LIBRARY TYPES
|
||||
|
||||
[[exercises]]
|
||||
name = "arc1"
|
||||
path = "exercises/standard_library_types/arc1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order
|
||||
to avoid creating a copy of `numbers`, you'll need to create `child_numbers`
|
||||
inside the loop but still in the main thread.
|
||||
|
||||
`child_numbers` should be a clone of the Arc of the numbers instead of a
|
||||
thread-local copy of the numbers."""
|
||||
|
||||
[[exercises]]
|
||||
name = "iterators2"
|
||||
path = "exercises/standard_library_types/iterators2.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
Step 1
|
||||
You need to call something on `first` before it can be collected
|
||||
Currently its type is `char`. Have a look at the methods that are available on that type:
|
||||
https://doc.rust-lang.org/std/primitive.char.html
|
||||
|
||||
|
||||
Step 2
|
||||
First you'll need to turn the Vec into an iterator
|
||||
Then you'll need to apply your function unto each item in the vector
|
||||
P.s. Don't forget to collect() at the end!
|
||||
|
||||
|
||||
Step 3.
|
||||
This is very similar to the previous test. The only real change is that you will need to
|
||||
alter the type that collect is coerced into. For a bonus you could try doing this with a
|
||||
turbofish"""
|
||||
|
||||
[[exercises]]
|
||||
name = "iterators3"
|
||||
path = "exercises/standard_library_types/iterators3.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
Minor hint: In each of the two cases in the match in main, you can create x with either
|
||||
a 'turbofish' or by hinting the type of x to the compiler. You may try both.
|
||||
|
||||
Major hint: Have a look at the Iter trait and at the explanation of its collect function.
|
||||
Especially the part about Result is interesting."""
|
||||
|
||||
[[exercises]]
|
||||
name = "iterators4"
|
||||
path = "exercises/standard_library_types/iterators4.rs"
|
||||
mode = "test"
|
||||
hint = """
|
||||
In an imperative language you might write a for loop to iterate through
|
||||
multiply the values into a mutable variable. Or you might write code more
|
||||
functionally with recursion and a match clause. But you can also use ranges
|
||||
and iterators to solve this in rust."""
|
||||
|
||||
# THREADS
|
||||
|
||||
[[exercises]]
|
||||
name = "threads1"
|
||||
path = "exercises/threads/threads1.rs"
|
||||
mode = "compile"
|
||||
hint = """
|
||||
`Arc` is an Atomic Reference Counted pointer that allows safe, shared access
|
||||
to **immutable** data. But we want to *change* the number of `jobs_completed`
|
||||
so we'll need to also use another type that will only allow one thread to
|
||||
mutate the data at a time. Take a look at this section of the book:
|
||||
https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct
|
||||
and keep reading if you'd like more hints :)
|
||||
|
||||
|
||||
Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like:
|
||||
`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));`
|
||||
Similar to the code in the example in the book that happens after the text
|
||||
that says "We can use Arc<T> to fix this.". If not, give that a try! If you
|
||||
do and would like more hints, keep reading!!
|
||||
|
||||
|
||||
Make sure neither of your threads are holding onto the lock of the mutex
|
||||
while they are sleeping, since this will prevent the other thread from
|
||||
being allowed to get the lock. Locks are automatically released when
|
||||
they go out of scope.
|
||||
|
||||
Ok, so, real talk, this was actually tricky for *me* to do too. And
|
||||
I could see a lot of different problems you might run into, so at this
|
||||
point I'm not sure which one you've hit :)
|
||||
|
||||
Please open an issue if you're still running into a problem that
|
||||
these hints are not helping you with, or if you've looked at the sample
|
||||
answers and don't understand why they work and yours doesn't.
|
||||
|
||||
If you've learned from the sample solutions, I encourage you to come
|
||||
back to this exercise and try it again in a few days to reinforce
|
||||
what you've learned :)"""
|
||||
|
||||
121
src/exercise.rs
121
src/exercise.rs
@@ -1,16 +1,20 @@
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fs::remove_file;
|
||||
use std::fs::{remove_file, File};
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command, Output};
|
||||
|
||||
const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
|
||||
const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE";
|
||||
const CONTEXT: usize = 2;
|
||||
|
||||
fn temp_file() -> String {
|
||||
format!("./temp_{}", process::id())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Copy, Clone)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Mode {
|
||||
Compile,
|
||||
@@ -24,8 +28,23 @@ pub struct ExerciseList {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Exercise {
|
||||
pub name: String,
|
||||
pub path: PathBuf,
|
||||
pub mode: Mode,
|
||||
pub hint: String,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum State {
|
||||
Done,
|
||||
Pending(Vec<ContextLine>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ContextLine {
|
||||
pub line: String,
|
||||
pub number: usize,
|
||||
pub important: bool,
|
||||
}
|
||||
|
||||
impl Exercise {
|
||||
@@ -52,6 +71,48 @@ impl Exercise {
|
||||
pub fn clean(&self) {
|
||||
let _ignored = remove_file(&temp_file());
|
||||
}
|
||||
|
||||
pub fn state(&self) -> State {
|
||||
let mut source_file =
|
||||
File::open(&self.path).expect("We were unable to open the exercise file!");
|
||||
|
||||
let source = {
|
||||
let mut s = String::new();
|
||||
source_file
|
||||
.read_to_string(&mut s)
|
||||
.expect("We were unable to read the exercise file!");
|
||||
s
|
||||
};
|
||||
|
||||
let re = Regex::new(I_AM_DONE_REGEX).unwrap();
|
||||
|
||||
if !re.is_match(&source) {
|
||||
return State::Done;
|
||||
}
|
||||
|
||||
let matched_line_index = source
|
||||
.lines()
|
||||
.enumerate()
|
||||
.filter_map(|(i, line)| if re.is_match(line) { Some(i) } else { None })
|
||||
.next()
|
||||
.expect("This should not happen at all");
|
||||
|
||||
let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize;
|
||||
let max_line = matched_line_index + CONTEXT;
|
||||
|
||||
let context = source
|
||||
.lines()
|
||||
.enumerate()
|
||||
.filter(|&(i, _)| i >= min_line && i <= max_line)
|
||||
.map(|(i, line)| ContextLine {
|
||||
line: line.to_string(),
|
||||
number: i + 1,
|
||||
important: i == matched_line_index,
|
||||
})
|
||||
.collect();
|
||||
|
||||
State::Pending(context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Exercise {
|
||||
@@ -63,17 +124,71 @@ impl Display for Exercise {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn test_clean() {
|
||||
File::create(&temp_file()).unwrap();
|
||||
let exercise = Exercise {
|
||||
name: String::from("example"),
|
||||
path: PathBuf::from("example.rs"),
|
||||
mode: Mode::Test,
|
||||
hint: String::from(""),
|
||||
};
|
||||
exercise.clean();
|
||||
assert!(!Path::new(&temp_file()).exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pending_state() {
|
||||
let exercise = Exercise {
|
||||
name: "pending_exercise".into(),
|
||||
path: PathBuf::from("tests/fixture/state/pending_exercise.rs"),
|
||||
mode: Mode::Compile,
|
||||
hint: String::new(),
|
||||
};
|
||||
|
||||
let state = exercise.state();
|
||||
let expected = vec![
|
||||
ContextLine {
|
||||
line: "// fake_exercise".to_string(),
|
||||
number: 1,
|
||||
important: false,
|
||||
},
|
||||
ContextLine {
|
||||
line: "".to_string(),
|
||||
number: 2,
|
||||
important: false,
|
||||
},
|
||||
ContextLine {
|
||||
line: "// I AM NOT DONE".to_string(),
|
||||
number: 3,
|
||||
important: true,
|
||||
},
|
||||
ContextLine {
|
||||
line: "".to_string(),
|
||||
number: 4,
|
||||
important: false,
|
||||
},
|
||||
ContextLine {
|
||||
line: "fn main() {".to_string(),
|
||||
number: 5,
|
||||
important: false,
|
||||
},
|
||||
];
|
||||
|
||||
assert_eq!(state, State::Pending(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_finished_exercise() {
|
||||
let exercise = Exercise {
|
||||
name: "finished_exercise".into(),
|
||||
path: PathBuf::from("tests/fixture/state/finished_exercise.rs"),
|
||||
mode: Mode::Compile,
|
||||
hint: String::new(),
|
||||
};
|
||||
|
||||
assert_eq!(exercise.state(), State::Done);
|
||||
}
|
||||
}
|
||||
|
||||
71
src/main.rs
71
src/main.rs
@@ -7,6 +7,7 @@ use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -25,8 +26,13 @@ fn main() {
|
||||
SubCommand::with_name("run")
|
||||
.alias("r")
|
||||
.about("Runs/Tests a single exercise")
|
||||
.arg(Arg::with_name("file").required(true).index(1))
|
||||
.arg(Arg::with_name("test").short("t").long("test").help("Run the file as a test")),
|
||||
.arg(Arg::with_name("name").required(true).index(1)),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("hint")
|
||||
.alias("h")
|
||||
.about("Returns a hint for the current exercise")
|
||||
.arg(Arg::with_name("name").required(true).index(1)),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
@@ -51,38 +57,48 @@ fn main() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !rustc_exists() {
|
||||
println!("We cannot find `rustc`.");
|
||||
println!("Try running `rustc --version` to diagnose your problem.");
|
||||
println!("For instructions on how to install Rust, check the README.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let toml_str = &fs::read_to_string("info.toml").unwrap();
|
||||
let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises;
|
||||
|
||||
if let Some(ref matches) = matches.subcommand_matches("run") {
|
||||
let filename = matches.value_of("file").unwrap_or_else(|| {
|
||||
println!("Please supply a file name!");
|
||||
std::process::exit(1);
|
||||
});
|
||||
let name = matches.value_of("name").unwrap();
|
||||
|
||||
let matching_exercise = |e: &&Exercise| {
|
||||
Path::new(filename)
|
||||
.canonicalize()
|
||||
.map(|p| p.ends_with(&e.path))
|
||||
.unwrap_or(false)
|
||||
};
|
||||
let matching_exercise = |e: &&Exercise| name == e.name;
|
||||
|
||||
let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| {
|
||||
println!("No exercise found for your file name!");
|
||||
println!("No exercise found for your given name!");
|
||||
std::process::exit(1)
|
||||
});
|
||||
|
||||
run(&exercise).unwrap_or_else(|_| std::process::exit(1));
|
||||
}
|
||||
|
||||
if let Some(ref matches) = matches.subcommand_matches("hint") {
|
||||
let name = matches.value_of("name").unwrap();
|
||||
|
||||
let exercise = exercises
|
||||
.iter()
|
||||
.find(|e| name == e.name)
|
||||
.unwrap_or_else(|| {
|
||||
println!("No exercise found for your given name!");
|
||||
std::process::exit(1)
|
||||
});
|
||||
|
||||
println!("{}", exercise.hint);
|
||||
}
|
||||
|
||||
if matches.subcommand_matches("verify").is_some() {
|
||||
verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
|
||||
}
|
||||
|
||||
if matches.subcommand_matches("watch").is_some() {
|
||||
/* Clears the terminal with an ANSI escape code.
|
||||
Works in UNIX and newer Windows terminals. */
|
||||
println!("\x1Bc");
|
||||
watch(&exercises).unwrap();
|
||||
}
|
||||
|
||||
@@ -93,11 +109,18 @@ fn main() {
|
||||
}
|
||||
|
||||
fn watch(exercises: &[Exercise]) -> notify::Result<()> {
|
||||
/* Clears the terminal with an ANSI escape code.
|
||||
Works in UNIX and newer Windows terminals. */
|
||||
fn clear_screen() {
|
||||
println!("\x1Bc");
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
||||
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
|
||||
|
||||
clear_screen();
|
||||
let _ignored = verify(exercises.iter());
|
||||
|
||||
loop {
|
||||
@@ -105,12 +128,12 @@ fn watch(exercises: &[Exercise]) -> notify::Result<()> {
|
||||
Ok(event) => match event {
|
||||
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
||||
println!("----------**********----------\n");
|
||||
let filepath = b.as_path().canonicalize().unwrap();
|
||||
let exercise = exercises
|
||||
let pending_exercises = exercises
|
||||
.iter()
|
||||
.skip_while(|e| !filepath.ends_with(&e.path));
|
||||
let _ignored = verify(exercise);
|
||||
clear_screen();
|
||||
let _ignored = verify(pending_exercises);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -119,3 +142,13 @@ fn watch(exercises: &[Exercise]) -> notify::Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rustc_exists() -> bool {
|
||||
Command::new("rustc")
|
||||
.args(&["--version"])
|
||||
.stdout(Stdio::null())
|
||||
.spawn()
|
||||
.and_then(|mut child| child.wait())
|
||||
.map(|status| status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
use crate::exercise::{Exercise, Mode};
|
||||
use crate::exercise::{Exercise, Mode, State};
|
||||
use console::{style, Emoji};
|
||||
use indicatif::ProgressBar;
|
||||
|
||||
pub fn verify<'a>(start_at: impl IntoIterator<Item = &'a Exercise>) -> Result<(), ()> {
|
||||
for exercise in start_at {
|
||||
match exercise.mode {
|
||||
Mode::Test => test(&exercise)?,
|
||||
let is_done = match exercise.mode {
|
||||
Mode::Test => compile_and_test_interactively(&exercise)?,
|
||||
Mode::Compile => compile_only(&exercise)?,
|
||||
};
|
||||
if !is_done {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_only(exercise: &Exercise) -> Result<(), ()> {
|
||||
pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||
compile_and_test(exercise, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
|
||||
let progress_bar = ProgressBar::new_spinner();
|
||||
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
|
||||
progress_bar.enable_steady_tick(100);
|
||||
@@ -22,7 +30,7 @@ fn compile_only(exercise: &Exercise) -> Result<(), ()> {
|
||||
let formatstr = format!("{} Successfully compiled {}!", Emoji("✅", "✓"), exercise);
|
||||
println!("{}", style(formatstr).green());
|
||||
exercise.clean();
|
||||
Ok(())
|
||||
Ok(prompt_for_completion(&exercise))
|
||||
} else {
|
||||
let formatstr = format!(
|
||||
"{} Compilation of {} failed! Compiler error message:\n",
|
||||
@@ -36,7 +44,11 @@ fn compile_only(exercise: &Exercise) -> Result<(), ()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||
fn compile_and_test_interactively(exercise: &Exercise) -> Result<bool, ()> {
|
||||
compile_and_test(exercise, false)
|
||||
}
|
||||
|
||||
fn compile_and_test(exercise: &Exercise, skip_prompt: bool) -> Result<bool, ()> {
|
||||
let progress_bar = ProgressBar::new_spinner();
|
||||
progress_bar.set_message(format!("Testing {}...", exercise).as_str());
|
||||
progress_bar.enable_steady_tick(100);
|
||||
@@ -52,7 +64,7 @@ pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||
let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), exercise);
|
||||
println!("{}", style(formatstr).green());
|
||||
exercise.clean();
|
||||
Ok(())
|
||||
Ok(skip_prompt || prompt_for_completion(exercise))
|
||||
} else {
|
||||
let formatstr = format!(
|
||||
"{} Testing of {} failed! Please try again. Here's the output:",
|
||||
@@ -77,3 +89,41 @@ pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt_for_completion(exercise: &Exercise) -> bool {
|
||||
let context = match exercise.state() {
|
||||
State::Done => return true,
|
||||
State::Pending(context) => context,
|
||||
};
|
||||
|
||||
let success_msg = match exercise.mode {
|
||||
Mode::Compile => "The code is compiling!",
|
||||
Mode::Test => "The code is compiling, and the tests pass!",
|
||||
};
|
||||
|
||||
println!("");
|
||||
println!("🎉 🎉 {} 🎉 🎉", success_msg);
|
||||
println!("");
|
||||
println!("You can keep working on this exercise,");
|
||||
println!(
|
||||
"or jump into the next one by removing the {} comment:",
|
||||
style("`I AM NOT DONE`").bold()
|
||||
);
|
||||
println!("");
|
||||
for context_line in context {
|
||||
let formatted_line = if context_line.important {
|
||||
format!("{}", style(context_line.line).bold())
|
||||
} else {
|
||||
format!("{}", context_line.line)
|
||||
};
|
||||
|
||||
println!(
|
||||
"{:>2} {} {}",
|
||||
style(context_line.number).blue().bold(),
|
||||
style("|").blue(),
|
||||
formatted_line
|
||||
);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
[[exercises]]
|
||||
name = "compFailure"
|
||||
path = "compFailure.rs"
|
||||
mode = "compile"
|
||||
hint = ""
|
||||
|
||||
[[exercises]]
|
||||
name = "testFailure"
|
||||
path = "testFailure.rs"
|
||||
mode = "test"
|
||||
mode = "test"
|
||||
hint = "Hello!"
|
||||
|
||||
5
tests/fixture/state/finished_exercise.rs
Normal file
5
tests/fixture/state/finished_exercise.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// fake_exercise
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
11
tests/fixture/state/info.toml
Normal file
11
tests/fixture/state/info.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[[exercises]]
|
||||
name = "pending_exercise"
|
||||
path = "pending_exercise.rs"
|
||||
mode = "compile"
|
||||
hint = """"""
|
||||
|
||||
[[exercises]]
|
||||
name = "pending_test_exercise"
|
||||
path = "pending_test_exercise.rs"
|
||||
mode = "test"
|
||||
hint = """"""
|
||||
7
tests/fixture/state/pending_exercise.rs
Normal file
7
tests/fixture/state/pending_exercise.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// fake_exercise
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
4
tests/fixture/state/pending_test_exercise.rs
Normal file
4
tests/fixture/state/pending_test_exercise.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
// I AM NOT DONE
|
||||
|
||||
#[test]
|
||||
fn it_works() {}
|
||||
@@ -1,7 +1,11 @@
|
||||
[[exercises]]
|
||||
name = "compSuccess"
|
||||
path = "compSuccess.rs"
|
||||
mode = "compile"
|
||||
hint = """"""
|
||||
|
||||
[[exercises]]
|
||||
name = "testSuccess"
|
||||
path = "testSuccess.rs"
|
||||
mode = "test"
|
||||
mode = "test"
|
||||
hint = """"""
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use assert_cmd::prelude::*;
|
||||
use glob::glob;
|
||||
use predicates::boolean::PredicateBooleanExt;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::process::Command;
|
||||
|
||||
#[test]
|
||||
@@ -40,7 +44,7 @@ fn verify_all_failure() {
|
||||
fn run_single_compile_success() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "compSuccess.rs"])
|
||||
.args(&["r", "compSuccess"])
|
||||
.current_dir("tests/fixture/success/")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -50,7 +54,7 @@ fn run_single_compile_success() {
|
||||
fn run_single_compile_failure() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "compFailure.rs"])
|
||||
.args(&["r", "compFailure"])
|
||||
.current_dir("tests/fixture/failure/")
|
||||
.assert()
|
||||
.code(1);
|
||||
@@ -60,7 +64,7 @@ fn run_single_compile_failure() {
|
||||
fn run_single_test_success() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "testSuccess.rs"])
|
||||
.args(&["r", "testSuccess"])
|
||||
.current_dir("tests/fixture/success/")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -70,7 +74,7 @@ fn run_single_test_success() {
|
||||
fn run_single_test_failure() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "testFailure.rs"])
|
||||
.args(&["r", "testFailure"])
|
||||
.current_dir("tests/fixture/failure/")
|
||||
.assert()
|
||||
.code(1);
|
||||
@@ -105,3 +109,53 @@ fn run_single_test_no_exercise() {
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_hint_for_single_test() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["h", "testFailure"])
|
||||
.current_dir("tests/fixture/failure")
|
||||
.assert()
|
||||
.code(0)
|
||||
.stdout("Hello!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_exercises_require_confirmation() {
|
||||
for exercise in glob("exercises/**/*.rs").unwrap() {
|
||||
let path = exercise.unwrap();
|
||||
let source = {
|
||||
let mut file = File::open(&path).unwrap();
|
||||
let mut s = String::new();
|
||||
file.read_to_string(&mut s).unwrap();
|
||||
s
|
||||
};
|
||||
source.matches("// I AM NOT DONE").next().expect(&format!(
|
||||
"There should be an `I AM NOT DONE` annotation in {:?}",
|
||||
path
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_compile_exercise_does_not_prompt() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "pending_exercise"])
|
||||
.current_dir("tests/fixture/state")
|
||||
.assert()
|
||||
.code(0)
|
||||
.stdout(predicates::str::contains("I AM NOT DONE").not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_test_exercise_does_not_prompt() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "pending_test_exercise"])
|
||||
.current_dir("tests/fixture/state")
|
||||
.assert()
|
||||
.code(0)
|
||||
.stdout(predicates::str::contains("I AM NOT DONE").not());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user