Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ look right, but you _will_ still be able to build the book. To use the plugins,
you should run:

```bash
$ cargo install --locked --path packages/mdbook-trpl
$ cargo install --locked --path packages/mdbook-trpl --force
```

## Building
Expand Down
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-01/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ async fn page_title(url: &str) -> Option<String> {
let response_text = response.text().await;
Html::parse(&response_text)
.select_first("title")
.map(|title_element| title_element.inner_html())
.map(|title| title.inner_html())
}
// ANCHOR_END: all
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-02/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ async fn page_title(url: &str) -> Option<String> {
// ANCHOR_END: chaining
Html::parse(&response_text)
.select_first("title")
.map(|title_element| title_element.inner_html())
.map(|title| title.inner_html())
}
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-03/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ async fn page_title(url: &str) -> Option<String> {
let response_text = trpl::get(url).await.text().await;
Html::parse(&response_text)
.select_first("title")
.map(|title_element| title_element.inner_html())
.map(|title| title.inner_html())
}
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-04/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ async fn page_title(url: &str) -> Option<String> {
let response_text = trpl::get(url).await.text().await;
Html::parse(&response_text)
.select_first("title")
.map(|title_element| title_element.inner_html())
.map(|title| title.inner_html())
}
8 changes: 4 additions & 4 deletions listings/ch17-async-await/listing-17-05/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ fn main() {

println!("{url} returned first");
match maybe_title {
Some(title) => println!("Its page title is: '{title}'"),
None => println!("Its title could not be parsed."),
Some(title) => println!("Its page title was: '{title}'"),
None => println!("It had no title."),
}
})
}

async fn page_title(url: &str) -> (&str, Option<String>) {
let text = trpl::get(url).await.text().await;
let title = Html::parse(&text)
let response_text = trpl::get(url).await.text().await;
let title = Html::parse(&response_text)
.select_first("title")
.map(|title| title.inner_html());
(url, title)
Expand Down
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-09/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn main() {
tx.send(val).unwrap();

let received = rx.recv().await.unwrap();
println!("Got: {received}");
println!("received '{received}'");
// ANCHOR_END: channel
});
}
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-28/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn main() {
"Finally finished"
};

match timeout(slow, Duration::from_millis(10)).await {
match timeout(slow, Duration::from_secs(2)).await {
Ok(message) => println!("Succeeded with '{message}'"),
Err(duration) => {
println!("Failed after {} seconds", duration.as_secs())
Expand Down
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-37/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ fn main() {
let messages = get_messages().timeout(Duration::from_millis(200));
let intervals = get_intervals();
let merged = messages.merge(intervals);
// ANCHOR_END: main

while let Some(result) = merged.next().await {
// ANCHOR_END: main
match result {
Ok(message) => println!("{message}"),
Err(reason) => eprintln!("Problem: {reason:?}"),
Expand Down
2 changes: 1 addition & 1 deletion listings/ch17-async-await/listing-17-38/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ fn main() {
.timeout(Duration::from_secs(10));
let merged = messages.merge(intervals);
let mut stream = pin!(merged);
// ANCHOR_END: main

while let Some(result) = stream.next().await {
// ANCHOR_END: main
match result {
Ok(message) => println!("{message}"),
Err(reason) => eprintln!("Problem: {reason:?}"),
Expand Down
Binary file modified nostarch/docx/chapter17.docx
Binary file not shown.
28 changes: 23 additions & 5 deletions packages/trpl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,36 @@ pub use tokio_stream::{
///
/// - Not *that* far off from what Tokio itself does under the hood in its own
/// `tokio::main` macro for supporting `async fn main`.
pub fn run<F: Future>(future: F) -> F::Output {
pub fn block_on<F: Future>(future: F) -> F::Output {
let rt = Runtime::new().unwrap();
rt.block_on(future)
}

/// This function has been renamed to `block_on`; please see its documentation.
/// This function remains to maintain compatibility with the online versions
/// of the book that use the name `run`.
pub fn run<F: Future>(future: F) -> F::Output {
block_on(future)
}

/// Run two futures, taking whichever finishes first and canceling the other.
///
/// Notice that this is built on [`futures::future::select`], which has the
/// same overall semantics but does *not* drop the slower future. The idea there
/// is that you can work with the first result and then later *also* continue
/// waiting for the second future.
///
/// We use the `race` semantics, where the slower future is simply dropped, for
/// the sake of simplicity in the examples: no need to deal with the tuple and
/// intentionally ignore the second future this way!
/// We drop the slower future for the sake of simplicity in the examples: no
/// need to deal with the tuple and intentionally ignore the second future this
/// way!
///
/// Note that this only works as “simply” as it does because:
///
/// - It takes ownership of the futures.
/// - It internally *pins* the futures.
/// - It throws away (rather than returning) the unused future (which is why it
/// can get away with pinning them).
pub async fn race<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
pub async fn select<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
where
F1: Future<Output = A>,
F2: Future<Output = B>,
Expand All @@ -97,6 +104,17 @@ where
}
}

/// This function has been renamed to `select`; please see its documentation.
/// This function remains to maintain compatibility with the online versions
/// of the book that use the name `race`.
pub async fn race<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
where
F1: Future<Output = A>,
F2: Future<Output = B>,
{
select(f1, f2).await
}

/// Fetch data from a URL. For more convenient use in _The Rust Programming
/// Language_, panics instead of returning a [`Result`] if the request fails.
pub async fn get(url: &str) -> Response {
Expand Down
69 changes: 52 additions & 17 deletions packages/trpl/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,30 @@ use std::{pin::Pin, time::Duration};
use futures::Future;
use trpl::{Either, Receiver, Sender};

/// This test is foundational for all the others, as they depend on `run`.
/// We initially named the function `run` and an online version of the async chapter
/// was released with that name, so we want to keep it working. We decided to rename
/// `run` to be `block_on` to more closely match other crates' names, so most of the
/// tests now use the `block_on` name.
#[test]
fn using_run_works() {
let val = trpl::run(async { "Hello" });
assert_eq!(val, "Hello");
}

/// This test is foundational for all the others, as they depend on `block_on`.
///
/// If we mess this up, *all* the tests below will fail -- so by the same token,
/// if all the tests below are failing, this one probably is too; fix it and the
/// others will likely start working again.
#[test]
fn re_exported_run_works() {
let val = trpl::run(async { "Hello" });
fn re_exported_block_on_works() {
let val = trpl::block_on(async { "Hello" });
assert_eq!(val, "Hello");
}

#[test]
fn re_exported_spawn_works() {
let result = trpl::run(async {
let result = trpl::block_on(async {
let handle_a = trpl::spawn_task(async { "Hello" });
let handle_b = trpl::spawn_task(async { "Goodbye" });
vec![handle_a.await.unwrap(), handle_b.await.unwrap()]
Expand All @@ -38,7 +48,7 @@ fn re_exported_spawn_works() {

#[test]
fn re_exported_sleep_works() {
let val = trpl::run(async {
let val = trpl::block_on(async {
trpl::sleep(Duration::from_micros(1)).await;
"Done!"
});
Expand All @@ -47,7 +57,7 @@ fn re_exported_sleep_works() {

#[test]
fn re_exported_channel_apis_work() {
trpl::run(async {
trpl::block_on(async {
// Explicitly naming the type to confirm the re-exports are aligned.
let (tx, mut rx): (Sender<&str>, Receiver<&str>) = trpl::channel();

Expand All @@ -67,7 +77,7 @@ mod re_exported_join_apis_work {

#[test]
fn join_fn() {
let result = trpl::run(async {
let result = trpl::block_on(async {
let a = async { 1 };
let b = async { 2 };
trpl::join(a, b).await
Expand All @@ -78,7 +88,7 @@ mod re_exported_join_apis_work {

#[test]
fn join3_fn() {
let result = trpl::run(async {
let result = trpl::block_on(async {
let a = async { 1 };
let b = async { 2 };
let c = async { 3 };
Expand All @@ -91,7 +101,7 @@ mod re_exported_join_apis_work {

#[test]
fn join_all_fn() {
let result = trpl::run(async {
let result = trpl::block_on(async {
let a = async { format!("{}", 1) };

let b = async { "Hello".to_string() };
Expand All @@ -117,7 +127,7 @@ mod re_exported_join_apis_work {

#[test]
fn join_macro() {
let result = trpl::run(async {
let result = trpl::block_on(async {
let a = async { 1 };
let b = async { "Hello" };

Expand All @@ -132,14 +142,39 @@ mod re_exported_join_apis_work {
}

#[test]
fn race() {
fn select() {
#[derive(Debug, PartialEq)]
struct Slow;

#[derive(Debug, PartialEq)]
struct Fast;

let val = trpl::block_on(async {
let slow = async {
trpl::sleep(Duration::from_millis(1_000)).await;
Slow
};

let fast = async {
trpl::sleep(Duration::from_millis(1)).await;
Fast
};

trpl::select(slow, fast).await
});

assert!(matches!(val, Either::Right(Fast)));
}

#[test]
fn race_continues_to_work() {
#[derive(Debug, PartialEq)]
struct Slow;

#[derive(Debug, PartialEq)]
struct Fast;

let val = trpl::run(async {
let val = trpl::block_on(async {
let slow = async {
trpl::sleep(Duration::from_millis(1_000)).await;
Slow
Expand All @@ -158,7 +193,7 @@ fn race() {

#[test]
fn yield_now() {
let result = trpl::run(async {
let result = trpl::block_on(async {
trpl::yield_now().await;
"done"
});
Expand All @@ -168,7 +203,7 @@ fn yield_now() {

#[test]
fn read_to_string() {
let result = trpl::run(async {
let result = trpl::block_on(async {
trpl::read_to_string("tests/integration/to-read.txt")
.await
.unwrap()
Expand All @@ -181,7 +216,7 @@ fn read_to_string() {
fn stream_iter() {
use trpl::StreamExt;

let result = trpl::run(async {
let result = trpl::block_on(async {
let ns = vec![1, 2, 3];
let mut stream = trpl::stream_from_iter(ns);
let mut result = vec![];
Expand All @@ -202,7 +237,7 @@ fn receiver_stream() {
use trpl::ReceiverStream;
use trpl::StreamExt;

let result: Vec<u32> = trpl::run(async {
let result: Vec<u32> = trpl::block_on(async {
println!("startup");
let (tx, rx) = trpl::channel();
let rx_stream = ReceiverStream::new(rx);
Expand All @@ -220,7 +255,7 @@ fn receiver_stream() {
fn re_exported_interval_stream_works() {
use trpl::{IntervalStream, StreamExt};

trpl::run(async {
trpl::block_on(async {
let mut interval_stream =
IntervalStream::new(trpl::interval(Duration::from_millis(1)))
.take(1);
Expand Down
2 changes: 1 addition & 1 deletion src/ch17-01-futures-and-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ in the document, if there is one. Because there may not be any matching element,
`Option::map` method, which lets us work with the item in the `Option` if it’s
present, and do nothing if it isn’t. (We could also use a `match` expression
here, but `map` is more idiomatic.) In the body of the function we supply to
`map`, we call `inner_html` on the `title_element` to get its content, which is
`map`, we call `inner_html` on the `title` to get its content, which is
a `String`. When all is said and done, we have an `Option<String>`.

Notice that Rust’s `await` keyword goes _after_ the expression you’re awaiting,
Expand Down