Add CI job to verify that we have no broken links. (#50)

Fix all broken links.
This commit is contained in:
Luca Palmieri 2024-05-24 16:45:59 +02:00 committed by GitHub
parent 6d707bb32d
commit f388b2a6c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 43 additions and 16 deletions

27
.github/workflows/ci.yml vendored Normal file
View File

@ -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

View File

@ -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
@ -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
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
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.

View File

@ -1,6 +1,6 @@
# 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:
```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.
## References

View File

@ -82,7 +82,7 @@ error: literal out of range for `i8`
As a rule of thumb, be quite careful with `as` casting.
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
[*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.
### 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
for primitive types and a few other special cases.
When working with composite types, you'll have to rely on
different conversion mechanisms ([fallible](../05_ticket_v2/13_try_from)
and [infallible](../04_traits/09_from)), which we'll explore later on.
different conversion mechanisms ([fallible](../05_ticket_v2/13_try_from.md)
and [infallible](../04_traits/09_from.md)), which we'll explore later on.
## References

View File

@ -38,7 +38,7 @@ let ticket = Ticket {
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
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
@ -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`
[^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).

View File

@ -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`
[^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
the pointers we discussed in this chapter, also known as **thin pointers**.

View File

@ -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?
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.
This allows for better error messages, better documentation, less unintentional breakages across versions,
and faster compilation times.

View File

@ -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
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
[a previous section](06_str_slice):
[a previous section](06_str_slice.md):
```rust
let mut s = String::with_capacity(5);

View File

@ -1,6 +1,6 @@
# 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:
1. reclaims the memory occupied by the type (i.e. `std::mem::size_of` bytes)

View File

@ -1,6 +1,6 @@
# 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`.
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:

View File

@ -102,7 +102,7 @@ async fn run() {
- 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**.
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
[`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