Render the book in PDF using pandoc and LaTeX. (#126)
* Render the book in PDF using `pandoc` and LaTeX. * Fix installs. * Go the apt-get route * Another attempt * Avoid installing twice. * Re-order. * Add more packages. * Minimise deps. Fix link checker. * Missing package. * Missing package. * Missing package. * More packages. * Missing package. * Missing package. * More packages... * Remove. * Fix link checker. * Fix link checker. * Fix path. * Add subtitle. * Avoid running over the right margin. * Avoid running over the right margin. * Formatting
This commit is contained in:
@@ -72,7 +72,8 @@ error: literal out of range for `i8`
|
||||
4 | let a = 255 as i8;
|
||||
| ^^^
|
||||
|
|
||||
= note: the literal `255` does not fit into the type `i8` whose range is `-128..=127`
|
||||
= note: the literal `255` does not fit into the type `i8`
|
||||
whose range is `-128..=127`
|
||||
= help: consider using the type `u8` instead
|
||||
= note: `#[deny(overflowing_literals)]` on by default
|
||||
```
|
||||
|
||||
@@ -48,7 +48,7 @@ You can create an instance of a struct by specifying the values for each field:
|
||||
// Syntax: <StructName> { <field_name>: <value>, ... }
|
||||
let ticket = Ticket {
|
||||
title: "Build a ticket system".into(),
|
||||
description: "Create a system that can manage tickets across a Kanban board".into(),
|
||||
description: "A Kanban board".into(),
|
||||
status: "Open".into()
|
||||
};
|
||||
```
|
||||
@@ -130,7 +130,8 @@ let default_config = Configuration::default();
|
||||
You can use the function call syntax even for methods that take `self` as their first parameter:
|
||||
|
||||
```rust
|
||||
// Function call syntax: <StructName>::<method_name>(<instance>, <parameters>)
|
||||
// Function call syntax:
|
||||
// <StructName>::<method_name>(<instance>, <parameters>)
|
||||
let is_open = Ticket::is_open(ticket);
|
||||
```
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ You have to use a **path** pointing to the entity you want to access.
|
||||
|
||||
You can compose the path in various ways:
|
||||
|
||||
- starting from the root of the current crate, e.g. `crate::module_1::module_2::MyStruct`
|
||||
- starting from the root of the current crate, e.g. `crate::module_1::MyStruct`
|
||||
- starting from the parent module, e.g. `super::my_function`
|
||||
- starting from the current module, e.g. `sub_module_1::MyStruct`
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ instantiation syntax:
|
||||
// This won't work!
|
||||
let ticket = Ticket {
|
||||
title: "Build a ticket system".into(),
|
||||
description: "Create a system that can manage tickets across a Kanban board".into(),
|
||||
description: "A Kanban board".into(),
|
||||
status: "Open".into()
|
||||
};
|
||||
```
|
||||
|
||||
@@ -44,9 +44,11 @@ error[E0382]: use of moved value: `ticket`
|
||||
| -------- `ticket` moved due to this method call
|
||||
...
|
||||
30 | println!("Your next task is: {}", ticket.title());
|
||||
| ^^^^^^ value used here after move
|
||||
| ^^^^^^
|
||||
| value used here after move
|
||||
|
|
||||
note: `Ticket::status` takes ownership of the receiver `self`, which moves `ticket`
|
||||
note: `Ticket::status` takes ownership of the receiver `self`,
|
||||
which moves `ticket`
|
||||
--> src/main.rs:12:23
|
||||
|
|
||||
12 | pub fn status(self) -> String {
|
||||
@@ -130,9 +132,11 @@ error[E0382]: use of moved value: `ticket`
|
||||
| -------- `ticket` moved due to this method call
|
||||
...
|
||||
30 | println!("Your next task is: {}", ticket.title());
|
||||
| ^^^^^^ value used here after move
|
||||
| ^^^^^^
|
||||
| value used here after move
|
||||
|
|
||||
note: `Ticket::status` takes ownership of the receiver `self`, which moves `ticket`
|
||||
note: `Ticket::status` takes ownership of the receiver `self`,
|
||||
which moves `ticket`
|
||||
--> src/main.rs:12:23
|
||||
|
|
||||
12 | pub fn status(self) -> String {
|
||||
@@ -199,8 +203,10 @@ fn main() {
|
||||
active: true,
|
||||
};
|
||||
// `b` is a reference to the `version` field of `config`.
|
||||
// The type of `b` is `&u32`, since it contains a reference to a `u32` value.
|
||||
// We create a reference by borrowing `config.version`, using the `&` operator.
|
||||
// The type of `b` is `&u32`, since it contains a reference to
|
||||
// a `u32` value.
|
||||
// We create a reference by borrowing `config.version`, using
|
||||
// the `&` operator.
|
||||
// Same symbol (`&`), different meaning depending on the context!
|
||||
let b: &u32 = &config.version;
|
||||
// ^ The type annotation is not necessary,
|
||||
|
||||
@@ -50,7 +50,11 @@ It takes ownership of `self`, changes the title, and returns the modified `Ticke
|
||||
This is how you'd use it:
|
||||
|
||||
```rust
|
||||
let ticket = Ticket::new("Title".into(), "Description".into(), "To-Do".into());
|
||||
let ticket = Ticket::new(
|
||||
"Title".into(),
|
||||
"Description".into(),
|
||||
"To-Do".into()
|
||||
);
|
||||
let ticket = ticket.set_title("New title".into());
|
||||
```
|
||||
|
||||
@@ -88,7 +92,11 @@ Nothing is returned.
|
||||
You'd use it like this:
|
||||
|
||||
```rust
|
||||
let mut ticket = Ticket::new("Title".into(), "Description".into(), "To-Do".into());
|
||||
let mut ticket = Ticket::new(
|
||||
"Title".into(),
|
||||
"Description".into(),
|
||||
"To-Do".into()
|
||||
);
|
||||
ticket.set_title("New title".into());
|
||||
|
||||
// Use the modified ticket
|
||||
|
||||
@@ -18,11 +18,25 @@ the function's arguments, local variables and a few "bookkeeping" values.\
|
||||
When the function returns, the stack frame is popped off the stack[^stack-overflow].
|
||||
|
||||
```text
|
||||
+-----------------+
|
||||
func2 | frame for func2 | func2
|
||||
+-----------------+ is called +-----------------+ returns +-----------------+
|
||||
| frame for func1 | -----------> | frame for func1 | ---------> | frame for func1 |
|
||||
+-----------------+ +-----------------+ +-----------------+
|
||||
+-----------------+
|
||||
| frame for func1 |
|
||||
+-----------------+
|
||||
|
|
||||
| func2 is
|
||||
| called
|
||||
v
|
||||
+-----------------+
|
||||
| frame for func2 |
|
||||
+-----------------+
|
||||
| frame for func1 |
|
||||
+-----------------+
|
||||
|
|
||||
| func2
|
||||
| returns
|
||||
v
|
||||
+-----------------+
|
||||
| frame for func1 |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
From an operational point of view, stack allocation/de-allocation is **very fast**.\
|
||||
|
||||
@@ -94,7 +94,8 @@ fn print_if_even<T>(n: T) {
|
||||
This code won't compile:
|
||||
|
||||
```text
|
||||
error[E0599]: no method named `is_even` found for type parameter `T` in the current scope
|
||||
error[E0599]: no method named `is_even` found for type parameter `T`
|
||||
in the current scope
|
||||
--> src/lib.rs:2:10
|
||||
|
|
||||
1 | fn print_if_even<T>(n: T) {
|
||||
@@ -106,7 +107,9 @@ error[E0277]: `T` doesn't implement `Debug`
|
||||
--> src/lib.rs:3:19
|
||||
|
|
||||
3 | println!("{n:?} is even");
|
||||
| ^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
| ^^^^^
|
||||
| `T` cannot be formatted using `{:?}` because
|
||||
| it doesn't implement `Debug`
|
||||
|
|
||||
help: consider restricting type parameter `T`
|
||||
|
|
||||
|
||||
@@ -72,7 +72,8 @@ You can, for example, create a `&str` from a `String` like this:
|
||||
```rust
|
||||
let mut s = String::with_capacity(5);
|
||||
s.push_str("Hello");
|
||||
// Create a string slice reference from the `String`, skipping the first byte.
|
||||
// Create a string slice reference from the `String`,
|
||||
// skipping the first byte.
|
||||
let slice: &str = &s[1..];
|
||||
```
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ to the pointer: the length of the slice it points to. Going back to the example
|
||||
```rust
|
||||
let mut s = String::with_capacity(5);
|
||||
s.push_str("Hello");
|
||||
// Create a string slice reference from the `String`, skipping the first byte.
|
||||
// Create a string slice reference from the `String`,
|
||||
// skipping the first byte.
|
||||
let slice: &str = &s[1..];
|
||||
```
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
Let's go back to where our string journey started:
|
||||
|
||||
```rust
|
||||
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
let ticket = Ticket::new(
|
||||
"A title".into(),
|
||||
"A description".into(),
|
||||
"To-Do".into()
|
||||
);
|
||||
```
|
||||
|
||||
We now know enough to start unpacking what `.into()` is doing here.
|
||||
@@ -14,7 +18,11 @@ This is the signature of the `new` method:
|
||||
|
||||
```rust
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: String) -> Self {
|
||||
pub fn new(
|
||||
title: String,
|
||||
description: String,
|
||||
status: String
|
||||
) -> Self {
|
||||
// [...]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ impl Drop for MyType {
|
||||
The compiler will complain with this error message:
|
||||
|
||||
```text
|
||||
error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
|
||||
error[E0184]: the trait `Copy` cannot be implemented for this type;
|
||||
the type has a destructor
|
||||
--> src/lib.rs:2:17
|
||||
|
|
||||
2 | #[derive(Clone, Copy)]
|
||||
|
||||
@@ -14,7 +14,11 @@ pub struct Ticket {
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: String) -> Self {
|
||||
pub fn new(
|
||||
title: String,
|
||||
description: String,
|
||||
status: String
|
||||
) -> Self {
|
||||
// [...]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ impl Ticket {
|
||||
match &self.status {
|
||||
Status::InProgress { assigned_to } => assigned_to,
|
||||
Status::Done | Status::ToDo => {
|
||||
panic!("Only `In-Progress` tickets can be assigned to someone")
|
||||
panic!(
|
||||
"Only `In-Progress` tickets can be \
|
||||
assigned to someone"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +36,9 @@ impl Ticket {
|
||||
if let Status::InProgress { assigned_to } = &self.status {
|
||||
assigned_to
|
||||
} else {
|
||||
panic!("Only `In-Progress` tickets can be assigned to someone");
|
||||
panic!(
|
||||
"Only `In-Progress` tickets can be assigned to someone"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +53,9 @@ you can use the `let/else` construct:
|
||||
impl Ticket {
|
||||
pub fn assigned_to(&self) -> &str {
|
||||
let Status::InProgress { assigned_to } = &self.status else {
|
||||
panic!("Only `In-Progress` tickets can be assigned to someone");
|
||||
panic!(
|
||||
"Only `In-Progress` tickets can be assigned to someone"
|
||||
);
|
||||
};
|
||||
assigned_to
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@ Let's revisit the `Ticket::new` function from the previous exercise:
|
||||
|
||||
```rust
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: Status) -> Ticket {
|
||||
pub fn new(
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status
|
||||
) -> Ticket {
|
||||
if title.is_empty() {
|
||||
panic!("Title cannot be empty");
|
||||
}
|
||||
@@ -70,8 +74,9 @@ Rust, with `Result`, forces you to **encode fallibility in the function's signat
|
||||
If a function can fail (and you want the caller to have a shot at handling the error), it must return a `Result`.
|
||||
|
||||
```rust
|
||||
// Just by looking at the signature, you know that this function can fail.
|
||||
// You can also inspect `ParseIntError` to see what kind of failures to expect.
|
||||
// Just by looking at the signature, you know that this function
|
||||
// can fail. You can also inspect `ParseIntError` to see what
|
||||
// kind of failures to expect.
|
||||
fn parse_int(s: &str) -> Result<i32, ParseIntError> {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ fn parse_int(s: &str) -> Result<i32, ParseIntError> {
|
||||
}
|
||||
|
||||
// This won't compile: we're not handling the error case.
|
||||
// We must either use `match` or one of the combinators provided by `Result`
|
||||
// to "unwrap" the success value or handle the error.
|
||||
// We must either use `match` or one of the combinators provided by
|
||||
// `Result` to "unwrap" the success value or handle the error.
|
||||
let number = parse_int("42") + 2;
|
||||
```
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ error[E0597]: `v` does not live long enough
|
||||
...
|
||||
15 | let right = &v[split_point..];
|
||||
| ^ borrowed value does not live long enough
|
||||
16 | let left_handle = thread::spawn(move || left.iter().sum::<i32>());
|
||||
| ------------------------------------------------
|
||||
argument requires that `v` is borrowed for `'static`
|
||||
16 | let left_handle = spawn(move || left.iter().sum::<i32>());
|
||||
| --------------------------------
|
||||
argument requires that `v` is borrowed for `'static`
|
||||
19 | }
|
||||
| - `v` dropped here while still borrowed
|
||||
```
|
||||
|
||||
@@ -122,8 +122,10 @@ error[E0277]: `MutexGuard<'_, i32>` cannot be sent between threads safely
|
||||
12 | | });
|
||||
| |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely
|
||||
|
|
||||
= help: the trait `Send` is not implemented for `MutexGuard<'_, i32>`, which is required by `{closure@src/main.rs:10:7: 10:14}: Send`
|
||||
= note: required for `std::sync::mpsc::Receiver<MutexGuard<'_, i32>>` to implement `Send`
|
||||
= help: the trait `Send` is not implemented for `MutexGuard<'_, i32>`,
|
||||
which is required by `{closure@src/main.rs:10:7: 10:14}: Send`
|
||||
= note: required for `std::sync::mpsc::Receiver<MutexGuard<'_, i32>>`
|
||||
to implement `Send`
|
||||
note: required because it's used within this closure
|
||||
```
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ note: future is not `Send` as this value is used across an await
|
||||
| -------- has type `Rc<i32>` which is not `Send`
|
||||
12 | // A `.await` point
|
||||
13 | yield_now().await;
|
||||
| ^^^^^ await occurs here, with `non_send` maybe used later
|
||||
| ^^^^^
|
||||
| await occurs here, with `non_send` maybe used later
|
||||
note: required by a bound in `tokio::spawn`
|
||||
|
|
||||
164 | pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
|
||||
@@ -84,7 +85,10 @@ trait Future {
|
||||
type Output;
|
||||
|
||||
// Ignore `Pin` and `Context` for now
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
|
||||
fn poll(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>
|
||||
) -> Poll<Self::Output>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -105,5 +105,5 @@ async fn run() {
|
||||
Check out [`select!`'s documentation](https://tokio.rs/tokio/tutorial/select) for more details.\
|
||||
If you need to interleave two asynchronous streams of data (e.g. a socket and a channel), prefer using
|
||||
[`StreamExt::merge`](https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.merge) instead.
|
||||
- Rather than "abrupt" cancellation, it can be preferable to rely
|
||||
on [`CancellationToken`](https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html).
|
||||
- A [`CancellationToken`](https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html) may be
|
||||
preferable to `JoinHandle::abort` in some cases.
|
||||
|
||||
@@ -48,5 +48,5 @@ check out the [Embedded Rust book](https://docs.rust-embedded.org/book/).
|
||||
|
||||
You can then find resources on key topics that cut across domains.\
|
||||
For testing, check out
|
||||
["Advanced testing, going beyond the basics"](https://github.com/mainmatter/rust-advanced-testing-workshop).\
|
||||
For telemetry, check out ["You can't fix what you can't see"](https://github.com/mainmatter/rust-telemetry-workshop).
|
||||
["Advanced testing, going beyond the basics"](https://rust-exercises.com/advanced-testing/).\
|
||||
For telemetry, check out ["You can't fix what you can't see"](https://rust-exercises.com/telemetry/).
|
||||
|
||||
Reference in New Issue
Block a user