Add CI job to verify that we have no broken links. (#50)
Fix all broken links.
This commit is contained in:
parent
6d707bb32d
commit
f388b2a6c3
|
@ -0,0 +1,27 @@
|
||||||
|
name: "CI"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-links:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Build book
|
||||||
|
run: |
|
||||||
|
cd book
|
||||||
|
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.40/mdbook-v0.4.40-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=.
|
||||||
|
./mdbook build
|
||||||
|
- name: Link Checker
|
||||||
|
uses: lycheeverse/lychee-action@v1
|
||||||
|
with:
|
||||||
|
fail: true
|
||||||
|
args: |
|
||||||
|
--exclude-loopback
|
||||||
|
--require-https
|
||||||
|
--no-progress
|
||||||
|
book/book
|
|
@ -117,7 +117,7 @@ error[E0308]: mismatched types
|
||||||
|
|
|
|
||||||
```
|
```
|
||||||
|
|
||||||
We'll see how to convert between types [later in this course](../04_traits/09_from).
|
We'll see how to convert between types [later in this course](../04_traits/09_from.md).
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
@ -131,8 +131,8 @@ We'll see how to convert between types [later in this course](../04_traits/09_fr
|
||||||
|
|
||||||
[^traits]: Rust doesn't let you define custom operators, but it puts you in control of how the built-in operators
|
[^traits]: Rust doesn't let you define custom operators, but it puts you in control of how the built-in operators
|
||||||
behave.
|
behave.
|
||||||
We'll talk about operator overloading [later in the course](../04_traits/03_operator_overloading), after we've covered traits.
|
We'll talk about operator overloading [later in the course](../04_traits/03_operator_overloading.md), after we've covered traits.
|
||||||
|
|
||||||
[^coercion]: There are some exceptions to this rule, mostly related to references, smart pointers and ergonomics. We'll
|
[^coercion]: There are some exceptions to this rule, mostly related to references, smart pointers and ergonomics. We'll
|
||||||
cover those [later on](../04_traits/07_deref).
|
cover those [later on](../04_traits/07_deref.md).
|
||||||
A mental model of "all conversions are explicit" will serve you well in the meantime.
|
A mental model of "all conversions are explicit" will serve you well in the meantime.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Panics
|
# Panics
|
||||||
|
|
||||||
Let's go back to the `speed` function you wrote for the ["Variables" section](02_variables).
|
Let's go back to the `speed` function you wrote for the ["Variables" section](02_variables.md).
|
||||||
It probably looked something like this:
|
It probably looked something like this:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -38,7 +38,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
There are other mechanisms to work with recoverable errors in Rust, which [we'll cover later](../05_ticket_v2/06_fallibility).
|
There are other mechanisms to work with recoverable errors in Rust, which [we'll cover later](../05_ticket_v2/06_fallibility.md).
|
||||||
For the time being we'll stick with panics as a brutal but simple stopgap solution.
|
For the time being we'll stick with panics as a brutal but simple stopgap solution.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
|
@ -82,7 +82,7 @@ error: literal out of range for `i8`
|
||||||
As a rule of thumb, be quite careful with `as` casting.
|
As a rule of thumb, be quite careful with `as` casting.
|
||||||
Use it _exclusively_ for going from a smaller type to a larger type.
|
Use it _exclusively_ for going from a smaller type to a larger type.
|
||||||
To convert from a larger to smaller integer type, rely on the
|
To convert from a larger to smaller integer type, rely on the
|
||||||
[*fallible* conversion machinery](../05_ticket_v2/13_try_from) that we'll
|
[*fallible* conversion machinery](../05_ticket_v2/13_try_from.md) that we'll
|
||||||
explore later in the course.
|
explore later in the course.
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
@ -91,8 +91,8 @@ Surprising behaviour is not the only downside of `as` casting.
|
||||||
It is also fairly limited: you can only rely on `as` casting
|
It is also fairly limited: you can only rely on `as` casting
|
||||||
for primitive types and a few other special cases.
|
for primitive types and a few other special cases.
|
||||||
When working with composite types, you'll have to rely on
|
When working with composite types, you'll have to rely on
|
||||||
different conversion mechanisms ([fallible](../05_ticket_v2/13_try_from)
|
different conversion mechanisms ([fallible](../05_ticket_v2/13_try_from.md)
|
||||||
and [infallible](../04_traits/09_from)), which we'll explore later on.
|
and [infallible](../04_traits/09_from.md)), which we'll explore later on.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ let ticket = Ticket {
|
||||||
You've seen this in action in the previous exercise on visibility.
|
You've seen this in action in the previous exercise on visibility.
|
||||||
We now need to provide one or more public **constructors**—i.e. static methods or functions that can be used
|
We now need to provide one or more public **constructors**—i.e. static methods or functions that can be used
|
||||||
from outside the module to create a new instance of the struct.
|
from outside the module to create a new instance of the struct.
|
||||||
Luckily enough we already have one: `Ticket::new`, as implemented in [a previous exercise](02_validation).
|
Luckily enough we already have one: `Ticket::new`, as implemented in [a previous exercise](02_validation.md).
|
||||||
|
|
||||||
## Accessor methods
|
## Accessor methods
|
||||||
|
|
||||||
|
@ -60,4 +60,4 @@ You have to write them yourself—they're just regular methods.
|
||||||
|
|
||||||
- The exercise for this section is located in `exercises/03_ticket_v1/05_encapsulation`
|
- The exercise for this section is located in `exercises/03_ticket_v1/05_encapsulation`
|
||||||
|
|
||||||
[^newtype]: Or refine their type, a technique we'll explore [later on](../05_ticket_v2/15_outro).
|
[^newtype]: Or refine their type, a technique we'll explore [later on](../05_ticket_v2/15_outro.md).
|
|
@ -49,6 +49,6 @@ They just point to a memory location, which _may_ be on the heap, but doesn't ha
|
||||||
|
|
||||||
- The exercise for this section is located in `exercises/03_ticket_v1/10_references_in_memory`
|
- The exercise for this section is located in `exercises/03_ticket_v1/10_references_in_memory`
|
||||||
|
|
||||||
[^fat]: [Later in the course](../04_traits/06_str_slice) we'll talk about **fat pointers**,
|
[^fat]: [Later in the course](../04_traits/06_str_slice.md) we'll talk about **fat pointers**,
|
||||||
i.e. pointers with additional metadata. As the name implies, they are larger than
|
i.e. pointers with additional metadata. As the name implies, they are larger than
|
||||||
the pointers we discussed in this chapter, also known as **thin pointers**.
|
the pointers we discussed in this chapter, also known as **thin pointers**.
|
||||||
|
|
|
@ -166,7 +166,7 @@ Follow Rust's conventions though: use camel case for type parameter names.
|
||||||
|
|
||||||
You may wonder why we need trait bounds at all. Can't the compiler infer the required traits from the function's body?
|
You may wonder why we need trait bounds at all. Can't the compiler infer the required traits from the function's body?
|
||||||
It could, but it won't.
|
It could, but it won't.
|
||||||
The rationale is the same as for [explicit type annotations on function parameters](../02_basic_calculator/02_variables#function-arguments-are-variables):
|
The rationale is the same as for [explicit type annotations on function parameters](../02_basic_calculator/02_variables.md#function-arguments-are-variables):
|
||||||
each function signature is a contract between the caller and the callee, and the terms must be explicitly stated.
|
each function signature is a contract between the caller and the callee, and the terms must be explicitly stated.
|
||||||
This allows for better error messages, better documentation, less unintentional breakages across versions,
|
This allows for better error messages, better documentation, less unintentional breakages across versions,
|
||||||
and faster compilation times.
|
and faster compilation times.
|
||||||
|
|
|
@ -6,7 +6,7 @@ From our previous [discussion on memory layouts](../03_ticket_v1/10_references_i
|
||||||
it would have been reasonable to expect `&str` to be represented as a single `usize` on
|
it would have been reasonable to expect `&str` to be represented as a single `usize` on
|
||||||
the stack, a pointer. That's not the case though. `&str` stores some **metadata** next
|
the stack, a pointer. That's not the case though. `&str` stores some **metadata** next
|
||||||
to the pointer: the length of the slice it points to. Going back to the example from
|
to the pointer: the length of the slice it points to. Going back to the example from
|
||||||
[a previous section](06_str_slice):
|
[a previous section](06_str_slice.md):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut s = String::with_capacity(5);
|
let mut s = String::with_capacity(5);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# The `Drop` trait
|
# The `Drop` trait
|
||||||
|
|
||||||
When we introduced [destructors](../03_ticket_v1/11_destructor),
|
When we introduced [destructors](../03_ticket_v1/11_destructor.md),
|
||||||
we mentioned that the `drop` function:
|
we mentioned that the `drop` function:
|
||||||
|
|
||||||
1. reclaims the memory occupied by the type (i.e. `std::mem::size_of` bytes)
|
1. reclaims the memory occupied by the type (i.e. `std::mem::size_of` bytes)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Enumerations
|
# Enumerations
|
||||||
|
|
||||||
Based on the validation logic you wrote [in a previous chapter](../03_ticket_v1/02_validation),
|
Based on the validation logic you wrote [in a previous chapter](../03_ticket_v1/02_validation.md),
|
||||||
there are only a few valid statuses for a ticket: `To-Do`, `InProgress` and `Done`.
|
there are only a few valid statuses for a ticket: `To-Do`, `InProgress` and `Done`.
|
||||||
This is not obvious if we look at the `status` field in the `Ticket` struct or at the type of the `status`
|
This is not obvious if we look at the `status` field in the `Ticket` struct or at the type of the `status`
|
||||||
parameter in the `new` method:
|
parameter in the `new` method:
|
||||||
|
|
|
@ -102,7 +102,7 @@ async fn run() {
|
||||||
|
|
||||||
- Be extremely careful when using `tokio`'s `select!` macro to "race" two different futures.
|
- Be extremely careful when using `tokio`'s `select!` macro to "race" two different futures.
|
||||||
Retrying the same task in a loop is dangerous unless you can ensure **cancellation safety**.
|
Retrying the same task in a loop is dangerous unless you can ensure **cancellation safety**.
|
||||||
Check out [`select!`'s documentation](https://docs.rs/tokio/macro.select.html) for more details.
|
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
|
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.
|
[`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
|
- Rather than "abrupt" cancellation, it can be preferable to rely
|
||||||
|
|
Loading…
Reference in New Issue