Compare commits
21 Commits
my_solutio
...
5ef0a6aa12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ef0a6aa12 | ||
|
|
f882f0416d | ||
|
|
6029a8fc17 | ||
|
|
056505d89f | ||
|
|
b039a6c5c2 | ||
|
|
3a9c9ea520 | ||
|
|
3f4d31148f | ||
|
|
2f067058ce | ||
|
|
be5c0e8bae | ||
|
|
a6056381bd | ||
|
|
59833f2a55 | ||
|
|
9a2086081c | ||
|
|
f272843c61 | ||
|
|
fccad08921 | ||
|
|
de45f8adf2 | ||
|
|
5660a2f7a8 | ||
|
|
491319a6d5 | ||
|
|
83cf1cad62 | ||
|
|
d8d7e73f1c | ||
|
|
468de3c0ac | ||
|
|
c86360f3c4 |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -39,18 +39,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: book
|
name: book
|
||||||
path: book/book
|
path: book/book
|
||||||
# Commit and push all changed files.
|
|
||||||
# Must only affect files that are listed in "paths-ignore".
|
|
||||||
- name: Git commit build artifacts
|
|
||||||
# Only run on main branch push (e.g. pull request merge).
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
run: |
|
|
||||||
git checkout -b deploy
|
|
||||||
git config --global user.name "Deployer"
|
|
||||||
git config --global user.email "username@users.noreply.github.com"
|
|
||||||
git add --force book/book
|
|
||||||
git commit -m "Render book"
|
|
||||||
git push --set-upstream --force-with-lease origin deploy
|
|
||||||
|
|
||||||
formatter:
|
formatter:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"]
|
members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
# This is needed to guarantee the expected behaviour on that specific exercise,
|
||||||
|
# regardless of the "global" setting for `overflow-checks` on the `dev` profile.
|
||||||
|
[profile.dev.package.copy]
|
||||||
|
overflow-checks = true
|
||||||
|
|||||||
@@ -112,3 +112,10 @@ where each name comes from and potentially introducing name conflicts.\
|
|||||||
Nonetheless, it can be useful in some cases, like when writing unit tests. You might have noticed
|
Nonetheless, it can be useful in some cases, like when writing unit tests. You might have noticed
|
||||||
that most of our test modules start with a `use super::*;` statement to bring all the items from the parent module
|
that most of our test modules start with a `use super::*;` statement to bring all the items from the parent module
|
||||||
(the one being tested) into scope.
|
(the one being tested) into scope.
|
||||||
|
|
||||||
|
## Visualizing the module tree
|
||||||
|
|
||||||
|
If you're struggling to picture the module tree of your project, you can try using
|
||||||
|
[`cargo-modules`](https://crates.io/crates/cargo-modules) to visualize it!
|
||||||
|
|
||||||
|
Refer to their documentation for installation instructions and usage examples.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ To enforce stricter rules, we must keep the fields private[^newtype].
|
|||||||
We can then provide public methods to interact with a `Ticket` instance.
|
We can then provide public methods to interact with a `Ticket` instance.
|
||||||
Those public methods will have the responsibility of upholding our invariants (e.g. a title must not be empty).
|
Those public methods will have the responsibility of upholding our invariants (e.g. a title must not be empty).
|
||||||
|
|
||||||
If all fields are private, it is no longer possible to create a `Ticket` instance directly using the struct
|
If at least one field is private it is no longer possible to create a `Ticket` instance directly using the struct
|
||||||
instantiation syntax:
|
instantiation syntax:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ let title = String::from("A title");
|
|||||||
|
|
||||||
We've been primarily using `.into()`, though.\
|
We've been primarily using `.into()`, though.\
|
||||||
If you check out the [implementors of `Into`](https://doc.rust-lang.org/std/convert/trait.Into.html#implementors)
|
If you check out the [implementors of `Into`](https://doc.rust-lang.org/std/convert/trait.Into.html#implementors)
|
||||||
you won't find `Into<&str> for String`. What's going on?
|
you won't find `Into<String> for &str`. What's going on?
|
||||||
|
|
||||||
`From` and `Into` are **dual traits**.\
|
`From` and `Into` are **dual traits**.\
|
||||||
In particular, `Into` is implemented for any type that implements `From` using a **blanket implementation**:
|
In particular, `Into` is implemented for any type that implements `From` using a **blanket implementation**:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ impl Ticket {
|
|||||||
match &self.status {
|
match &self.status {
|
||||||
Status::InProgress { assigned_to } => assigned_to,
|
Status::InProgress { assigned_to } => assigned_to,
|
||||||
Status::Done | Status::ToDo => {
|
Status::Done | Status::ToDo => {
|
||||||
panic!("Only `In-Progress` tickets can be assigned to someone"),
|
panic!("Only `In-Progress` tickets can be assigned to someone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,18 +46,3 @@ You can override these defaults by explicitly declaring your targets in the `Car
|
|||||||
[`cargo`'s documentation](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#cargo-targets) for more details.
|
[`cargo`'s documentation](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#cargo-targets) for more details.
|
||||||
|
|
||||||
Keep in mind that while a package can contain multiple crates, it can only contain one library crate.
|
Keep in mind that while a package can contain multiple crates, it can only contain one library crate.
|
||||||
|
|
||||||
## Scaffolding a new package
|
|
||||||
|
|
||||||
You can use `cargo` to scaffold a new package:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo new my-binary
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create a new folder, `my-binary`, containing a new Rust package with the same name and a single
|
|
||||||
binary crate inside. If you want to create a library crate instead, you can use the `--lib` flag:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo new my-library --lib
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ and why we might want to use them.
|
|||||||
## What is a thread?
|
## What is a thread?
|
||||||
|
|
||||||
A **thread** is an execution context managed by the underlying operating system.\
|
A **thread** is an execution context managed by the underlying operating system.\
|
||||||
Each thread has its own stack, instruction pointer, and program counter.
|
Each thread has its own stack and instruction pointer.
|
||||||
|
|
||||||
A single **process** can manage multiple threads.
|
A single **process** can manage multiple threads.
|
||||||
These threads share the same memory space, which means they can access the same data.
|
These threads share the same memory space, which means they can access the same data.
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ run out and crash with an out-of-memory error.
|
|||||||
fn oom_trigger() {
|
fn oom_trigger() {
|
||||||
loop {
|
loop {
|
||||||
let v: Vec<usize> = Vec::with_capacity(1024);
|
let v: Vec<usize> = Vec::with_capacity(1024);
|
||||||
Box::leak(v);
|
v.leak();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
At the same time, memory leaked via `Box::leak` is not truly forgotten.\
|
At the same time, memory leaked via `leak` method is not truly forgotten.\
|
||||||
The operating system can map each memory region to the process responsible for it.
|
The operating system can map each memory region to the process responsible for it.
|
||||||
When the process exits, the operating system will reclaim that memory.
|
When the process exits, the operating system will reclaim that memory.
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ In the non-threaded version of the system, updates were fairly straightforward:
|
|||||||
|
|
||||||
## Multithreaded updates
|
## Multithreaded updates
|
||||||
|
|
||||||
The same strategy won't work in the current multi-threaded version,
|
The same strategy won't work in the current multithreaded version. The borrow checker would
|
||||||
because the mutable reference would have to be sent over a channel. The borrow checker would
|
stop us: `SyncSender<&mut Ticket>` isn't `'static` because `&mut Ticket` doesn't satisfy the `'static` lifetime, therefore
|
||||||
stop us, because `&mut Ticket` doesn't satisfy the `'static` lifetime requirement of `SyncSender::send`.
|
they can't be captured by the closure that gets passed to `std::thread::spawn`.
|
||||||
|
|
||||||
There are a few ways to work around this limitation. We'll explore a few of them in the following exercises.
|
There are a few ways to work around this limitation. We'll explore a few of them in the following exercises.
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ fn main() {
|
|||||||
let guard = lock.lock().unwrap();
|
let guard = lock.lock().unwrap();
|
||||||
|
|
||||||
spawn(move || {
|
spawn(move || {
|
||||||
receiver.recv().unwrap();;
|
receiver.recv().unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Try to send the guard over the channel
|
// Try to send the guard over the channel
|
||||||
@@ -118,7 +118,7 @@ error[E0277]: `MutexGuard<'_, i32>` cannot be sent between threads safely
|
|||||||
| _-----_^
|
| _-----_^
|
||||||
| | |
|
| | |
|
||||||
| | required by a bound introduced by this call
|
| | required by a bound introduced by this call
|
||||||
11 | | receiver.recv().unwrap();;
|
11 | | receiver.recv().unwrap();
|
||||||
12 | | });
|
12 | | });
|
||||||
| |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely
|
| |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ pub async fn work() {
|
|||||||
|
|
||||||
### `std::thread::spawn` vs `tokio::spawn`
|
### `std::thread::spawn` vs `tokio::spawn`
|
||||||
|
|
||||||
You can think of `tokio::spawn` as the asynchronous sibling of `std::spawn::thread`.
|
You can think of `tokio::spawn` as the asynchronous sibling of `std::thread::spawn`.
|
||||||
|
|
||||||
Notice a key difference: with `std::thread::spawn`, you're delegating control to the OS scheduler.
|
Notice a key difference: with `std::thread::spawn`, you're delegating control to the OS scheduler.
|
||||||
You're not in control of how threads are scheduled.
|
You're not in control of how threads are scheduled.
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Yields to runtime
|
|||||||
Tries to acquire lock
|
Tries to acquire lock
|
||||||
```
|
```
|
||||||
|
|
||||||
We have a deadlock. Task B we'll never manage to acquire the lock, because the lock
|
We have a deadlock. Task B will never manage to acquire the lock, because the lock
|
||||||
is currently held by task A, which has yielded to the runtime before releasing the
|
is currently held by task A, which has yielded to the runtime before releasing the
|
||||||
lock and won't be scheduled again because the runtime cannot preempt task B.
|
lock and won't be scheduled again because the runtime cannot preempt task B.
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ mod tests {
|
|||||||
|
|
||||||
// You should be seeing this error when trying to run this exercise:
|
// You should be seeing this error when trying to run this exercise:
|
||||||
//
|
//
|
||||||
// error[E0616]: field `description` of struct `encapsulation::ticket::Ticket` is private
|
// error[E0616]: field `description` of struct `Ticket` is private
|
||||||
// |
|
// |
|
||||||
// | assert_eq!(ticket.description, "A description");
|
// | assert_eq!(ticket.description, "A description");
|
||||||
// | ^^^^^^^^^^^^^^^^^^
|
// | ^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ fn test_saturating_u16() {
|
|||||||
let b: SaturatingU16 = 5u8.into();
|
let b: SaturatingU16 = 5u8.into();
|
||||||
let c: SaturatingU16 = u16::MAX.into();
|
let c: SaturatingU16 = u16::MAX.into();
|
||||||
let d: SaturatingU16 = (&1u16).into();
|
let d: SaturatingU16 = (&1u16).into();
|
||||||
|
let e = &c;
|
||||||
|
|
||||||
assert_eq!(a + b, SaturatingU16::from(15u16));
|
assert_eq!(a + b, SaturatingU16::from(15u16));
|
||||||
assert_eq!(a + c, SaturatingU16::from(u16::MAX));
|
assert_eq!(a + c, SaturatingU16::from(u16::MAX));
|
||||||
assert_eq!(a + d, SaturatingU16::from(11u16));
|
assert_eq!(a + d, SaturatingU16::from(11u16));
|
||||||
assert_eq!(a + a, 20u16);
|
assert_eq!(a + a, 20u16);
|
||||||
assert_eq!(a + 5u16, 15u16);
|
assert_eq!(a + 5u16, 15u16);
|
||||||
assert_eq!(a + &u16::MAX, SaturatingU16::from(u16::MAX));
|
assert_eq!(a + e, SaturatingU16::from(u16::MAX));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn thirthieth() {
|
fn thirtieth() {
|
||||||
assert_eq!(fibonacci(30), 832040);
|
assert_eq!(fibonacci(30), 832040);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// TODO: Define a function named `lowercase` that converts all characters in a string to lowercase,
|
// TODO: Define a function named `squared` that raises all `i32`s within a slice to the power of 2.
|
||||||
// modifying the input in place.
|
// The slice should be modified in place.
|
||||||
// Does it need to take a `&mut String`? Does a `&mut [str]` work? Why or why not?
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -8,29 +7,22 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn empty() {
|
||||||
let mut s = String::from("");
|
let mut s = vec![];
|
||||||
lowercase(&mut s);
|
squared(&mut s);
|
||||||
assert_eq!(s, "");
|
assert_eq!(s, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_char() {
|
fn one() {
|
||||||
let mut s = String::from("A");
|
let mut s = [2];
|
||||||
lowercase(&mut s);
|
squared(&mut s);
|
||||||
assert_eq!(s, "a");
|
assert_eq!(s, [4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiple_chars() {
|
fn multiple() {
|
||||||
let mut s = String::from("Hello, World!");
|
let mut s = vec![2, 4];
|
||||||
lowercase(&mut s);
|
squared(&mut s);
|
||||||
assert_eq!(s, "hello, world!");
|
assert_eq!(s, vec![4, 16]);
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mut_slice() {
|
|
||||||
let mut s = "Hello, World!".to_string();
|
|
||||||
lowercase(s.as_mut_str());
|
|
||||||
assert_eq!(s, "hello, world!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ enum Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server(receiver: Receiver<Command>) {
|
fn server(receiver: Receiver<Command>) {
|
||||||
let mut store = TicketStore::new();
|
let mut store = TicketStore::new();
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
|
|||||||
Reference in New Issue
Block a user