Skip to content

Commit 6b3b4c0

Browse files
committed
Fix issues with chat app lesson
1 parent 6fee49f commit 6b3b4c0

File tree

2 files changed

+37
-28
lines changed

2 files changed

+37
-28
lines changed

sections/01_rust_projects/030_chat_app_backend/README.md

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,17 @@ libraries like it out there, so feel free to use whichever one you want. This
9393
section will cover how to set up and use axum, and you can find more information
9494
about it (or other frameworks) by looking at their documentation.
9595

96-
First, to install axum, run `cargo add axum` in your console. This will add it
97-
to your `Cargo.toml` and let you use it in your code. You also need to install
98-
tokio, which is an async runtime for Rust. We won't go too far into async/await
99-
in this lesson, but we will briefly touch on it and it is required for axum to
100-
function. You can install tokio with this command:
101-
`cargo add tokio --features full` (the `full` here gives you access to the full
102-
functionality of tokio). Finally, we'll need some features from the tower_http
103-
library — in particular, it can help us serve files in the `public` directory.
104-
Install it with `cargo add tower_http --features fs` (the `fs` here enables
105-
filesystem-related features that we need).
96+
First, to install axum, run `cargo add axum --features ws` in your console (the
97+
`ws` is to enable axum's WebSocket support). This will add it to your
98+
`Cargo.toml` and let you use it in your code. You also need to install tokio,
99+
which is an async runtime for Rust. We won't go too far into async/await in this
100+
lesson, but we will briefly touch on it and it is required for axum to function.
101+
You can install tokio with this command: `cargo add tokio --features full` (the
102+
`full` here gives you access to the full functionality of tokio). Finally, we'll
103+
need some features from the tower_http library — in particular, it can help us
104+
serve files in the `public` directory. Install it with
105+
`cargo add tower_http --features fs` (the `fs` here enables filesystem-related
106+
features that we need).
106107

107108
With all these dependencies, now's a good time to mention that you can run
108109
`cargo check` in the console to check for errors, instead of using `cargo run`
@@ -157,22 +158,18 @@ your router so that it looks like this:
157158

158159
```rust
159160
let app = Router::new()
160-
.nest_service("/", ServeDir::new("public"));
161+
.fallback_service(ServeDir::new("public"));
161162
```
162163

163164
You'll need to import some more things. Run `cargo check` to figure out how to
164-
do this. This line tells axum to use the service returned by
165-
`ServeDir::new("public")` to handle the `/` route. In other words, tower_http
166-
provides a `ServeDir` service that contains all the code needed to read from a
167-
directory, and will try to serve any files in it. If it can't find one that
168-
matches, then axum will try to match on other routes (right now, there aren't
169-
any).
170-
171-
What this means is that, when we try to connect to `/socket`, the web server
172-
will first look for a file matching `socket` in the `public` directory, see that
173-
there is none, and then move forward to our WebSocket handler. What this means
174-
is that, if you added a `socket` file to `public` directory, it would break the
175-
application.
165+
do this. Once you import everything, you'll probably get an error about type
166+
annotations. Don't worry! We'll fix that soon. This line above tells axum to use
167+
the service returned by `ServeDir::new("public")` to handle anything that hasn't
168+
already been handled. In other words, tower_http provides a `ServeDir` service
169+
that contains all the code needed to read from a directory, and will try to
170+
serve any files in it. First, axum will try the other routes (right now there
171+
aren't any), and if it can't find any matches, it'll look in the `public`
172+
directory.
176173

177174
Now, let's add the finishing touches to make everything work. Add the following
178175
lines after your router:
@@ -182,6 +179,8 @@ let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
182179
axum::serve(listener, app).await.unwrap();
183180
```
184181

182+
Make sure you import the `TcpListener` from tokio, not std!
183+
185184
The first line creates a listener on port `3000`, which is the port where the
186185
content will be served on. This means that you can access it at
187186
`http://localhost:3000`. This is also the port that Cratecode uses to serve
@@ -191,8 +190,8 @@ you're reading this on).
191190
Also, notice the `.await` on both of these lines. This means that the program
192191
will wait for the operation to finish until continuing. I won't delve into
193192
async/await right now, but just know that, whenever a function returns a
194-
`Future` type, you can `.await` it to retrieve the value (so long as you're
195-
inside an `async` function).
193+
`Future` type or is itself an `async` function, you can `.await` it to retrieve
194+
the value (so long as you're inside an `async` function).
196195

197196
Now's a good time to test it out and make sure it works. Try running the
198197
program, then open the web view tab (it's right above the console) and see if it
@@ -243,6 +242,9 @@ struct ServerState {
243242
}
244243
```
245244

245+
When importing, make sure you get `Arc` and `Mutex` from std, and `broadcast`
246+
from tokio!
247+
246248
The `#[derive(Clone)]` at the top tells Rust to automatically make this struct
247249
cloneable (which means we can duplicate it). The reason is that axum actually
248250
creates a copy of the server state every time it's needed. This means that if
@@ -408,11 +410,16 @@ async fn handle_socket(
408410
Here's some important information to get you started:
409411

410412
- To read/write data from/to `state.message_history`, do
411-
`state.message_history.lock()`. For example, you can do
412-
`state.message_history.lock().push("My message".to_string())`.
413+
`state.message_history.lock().unwrap()`. For example, you can do
414+
`state.message_history.lock().unwrap().push("My message".to_string())`.
413415
- Ensure that you aren't holding a lock across an await point or
414416
`tokio::select`. Doing so will cause issues that are hard to debug. Run
415-
`cargo clippy` to detect this (as well as other types of issues).
417+
`cargo clippy` to detect this (as well as other types of issues). You may have
418+
to clone your messages Vec to avoid this.
419+
- If you're getting error messages about `Send` bounds, it most likely means
420+
that you're holding a lock across an await point. To fix this, you can try
421+
introducing a variable, or wrapping the code with the lock inside it with a
422+
scope (`{}`).
416423
- You should have an infinite `loop` that your message receiving code goes
417424
through. Then, in each iteration of the loop, try to receive a message from
418425
the WebSocket or the broadcast channel. If the WebSocket returns `None`, then

sections/01_rust_projects/030_chat_app_backend/public/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
width: 100%;
4848
height: 100%;
4949

50+
min-height: 250px;
51+
5052
display: flex;
5153
flex-direction: column;
5254
}

0 commit comments

Comments
 (0)