Errata

Async Rust

The errata list is a list of errors and their corrections that were found after the product was released.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

Color Key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted by Date submitted
O'Reilly learning platform Page Chapter 1 "What is Async"
2nd paragraph after main example

"runs until it is interrupted or it is yielded by the CPU voluntarily"

The paragraph is attempting to talk about nuances in CPU design, but I don't understand how the statement above relates. An interrupt can cause a context switch to the operating system that can then schedule other work on the CPU, but I'm not sure what it means for a CPU to voluntarily yield. Is this trying to say that the thread returned with the calculated result?

Anonymous  Dec 16, 2023 
O'Reilly learning platform Page Chapter 1 "What is Async"
paragraph after figure 1-1

"If we were to use Tokio to join four Fibonacci computations we would not get an increase in speed as we are only using one thread."

This is misleading. If used similar to the Tokio reqwest example a multi-threaded runtime will be instantiated. Note the default behavior of the Tokio main macro is multi-threaded. The code sample below consumes 400% CPU. It would be more accurate to say "using one thread per task".

The point the author is trying to convey is that asynchrony does not speed up cpu-bound workloads. The following sentence gets back on track by describing that the work for one instance of the fibonacci calculation would need to be distributed across multiple cores.

#[tokio::main]
async fn main() {
let mut threads = Vec::new();

for i in 0..4 {
let handle = tokio::spawn(async move {
let result = fibonacci(4000);
println!("Thread {} result: {}", i, result);
});
threads.push(handle);
}
for handle in threads {
handle.await.unwrap();
}
}

fn fibonacci(n: u64) -> u64 {
if n == 0 || n == 1 {
return n;
}
fibonacci(n - 1) + fibonacci(n - 2)
}

Anonymous  Dec 16, 2023 
O'Reilly learning platform Page Chapter 1 "Introduction to Processes"
first paragraph

"A process is a program or application that is executed by the CPU."

A process is an abstraction provided by an operating system. It would be more correct to say it is executed by the operating system on a CPU. For a book focusing on asynchronous programming it seems like it would be important to clearly describe how the application makes use of OS-provided abstractions and functionality.

In the embedded space where applications run on bare metal asynchrony makes use of hardware interrupts but none of the context in this chapter suggests that's what's being discussed.

Anonymous  Dec 16, 2023 
O'Reilly learning platform Page ch2
Pinning in Futures

in the example 'SelfReferential'.
"If you try and run the code, you will get a segmentation fault. The segmentation fault is an error caused by accessing memory that does not belong to the program." which is true because it's accessing the "freed" stack memory of SelfReferential::new() after it has returned, the error has no relationships with the ptr::swap operation.

correct the example by moving the self_pointer manipulation on main()'s stack and then do the swap + print is more clear.

Bruce  May 11, 2024 
PDF Page p.36
1st paragraph

text says:
To then expose the danger of moving the struct by creating two instances of the `SelfReferential` struct, swap these instances in memory and print what data the raw pointer is pointing to with the following code:

What the code below actually doing is, creating a instance and then move it.
This is confusing.

HIDEMOTO NAKADA  Feb 23, 2025 
PDF Page p.43
in the middle

If the future is not pending (i.e., not ready), the executor places the task back into the queue to be executed in the future.

'not pending ' should be simply 'pending'.

HIDEMOTO NAKADA  Feb 23, 2025 
Printed Page p.27
1st code block

async fn prep_coffee_mug() {
sleep(Duration::from_millis(100)).await;
. . .
}

sleep -> thread::sleep

HIDEMOTO NAKADA  Feb 24, 2025 
Printed Page p.27
1st code block

async fn prep_coffee_mug() {
sleep(Duration::from_millis(100)).await;
. . .
}

sleep -> thread::sleep

HIDEMOTO NAKADA  Feb 24, 2025 
Printed Page p.67
second code block

std::env::set_var("LOW_NUM", self.low_num.to_string());

set_var is unsafe, so we need to have surrounding unsafe block.

HIDEMOTO NAKADA  Feb 26, 2025 
Printed Page p.9
in the middle

'Once we start using all our cores, the extra spawned processes will start to get blocked.'

This is wrong. The OS will take care the concurrent execution.

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.14
in the middle

The `shared_data_clone` thread is passed to `background_thread`,

the term thread should be removed since shared_data_clone is not a thread.

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.15
the last paragraph

Adding multiple `Condvars` for `updater_thread` to cycle ...

`updater_thread` should be `background_thread` .

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.30
l.2

If the count is at three

three should be five.

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.9
second paragraph

There can be limits to the number of threads we can spawn in an OS,

'threads' should be replaced with 'processes'.


HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.18
code in the middle


let _ = tx.send(());

this line does not compile since the channel is for boolean.

this should be replaced with

let _ = tx.send(true);

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.21
the output in the last line


'Logged in 2 days ago' should be printed out as the first line, as discussed in the following paragraph.

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.80
the paragraphs on the poll_write and the poll_flush

I believe there are several problems in these paragraphs.
- poll_write for the stream does not have a loop inside to ensure all the bytes are written.
It just returns the number of bytes written. This is not a problem since the caller will take care of it.
- poll_write and poll_flush have different roles. poll_write just copy the content in the buffer, and might flush the content to the destination. poll_flush just forces the flush. Note that the signature of these two functions are different.

HIDEMOTO NAKADA  Mar 29, 2025 
PDF Page p.128
the first paragraph of 'Building our event bus struct'

'Consumers also need to be able to unsubscribe to events.'

'events' should be replaced with 'subjects'.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.143
in the paragraph after 'thread_local' j

'We then need a simple async task that blocks the thread for a second, '

'a second' should be replaced with 'three seconds', since the code below says so.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.143
l.4

'This means we can use structs that do not have the Send trait implemented because we are ensuring that the task stays on a specific thread.’

I could not reproduce this. I believe spawn_pinned requires the Send trait to be implemented.
Could you please clarify this?

HIDEMOTO NAKADA   Mar 30, 2025 
PDF Page p.156
the last code fragment

if msg.responder.send(state).is_err() {

state should be cast into i64 since the type of state is i32.

if msg.responder.send(state.into()).is_err() {

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.168
the first paragraph

We need both actors to load the state on startup, so our file loading is defined by the following isolated function:

I understand only the Writer Actor accesses the file. The Key-Valure Actor just gets the state from the Writer Actor.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.170
The end of the first paragraph of 'Creating Actor Supervisor'

The supervisor can also send reset requests to the correct actor
through the browser, as depicted in Figure 8-3.

`browser` should be replaced with `supervisor`.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.187
the implementation of ExcitedGreeting.

the code does not implement the Greeting trait.
this should be replaced with

impl<T> Greeting for ExcitedGreeting<T> where T: Greeting{
fn greet(&self) -> String {
let mut greeting = self.inner.greet();
greeting.push_str(" I'm so excited to be in Rust!");
greeting
}
}

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.192
the first paragraph

However, we could replace this switch with a counter by using an AtomicUsize, as opposed to an AtomicBool, if we wanted.

AtomicBool is not used in the preceding code. I believe this is some kind of mistake.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.220
in the 4th paragraph

"We can now move on to our async function where we spawn a task,"

The following function is not async, so 'async' should be removed.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.223
the 4th paragraph

The logic of how we handle the result stays the same: we spawn the task before the print statement and get the result after the print statement.

The code does not have spawn / get result. I believe this is a mistake.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.223
the 'use' code block

use super::*;

In my code structure, this line is not required and I cannot compile the code with this import line.
What is the assumed code structure here?

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.228
the first paragraph

"In our test function, we can build a single-threaded runtime and spawn 10,000 unsafe_add tasks."

10,000 should be replaced with 100,000, since the code below spawns 100,000 tasks.

HIDEMOTO NAKADA  Mar 30, 2025 
PDF Page p.234
the last paragraph

"Here we can see with our assert_pending trait, "

'trait' should be replaced with 'macro', since assert_pending is a macro.

HIDEMOTO NAKADA  Mar 30, 2025 
Printed Page ch7 p148 "Getting Unsafe with Thread Data"
let repeated_sequence: Vec<_> = [...].take(5000).[...];

5000 should be 500_000 to match the following text "This gives us half a million async tasks" and to match the expected output of 200_000.

David Wallace Croft  Jun 13, 2025 
Printed Page 18
watch_file_changes() method code block

tx is Sender<bool> but used as tx.send(()). Correct by changing tx to Sender<()> and change on the following page watch::channel(false) to watch::channel(())

David Wallace Croft  May 04, 2025 
Printed Page 37
line 7

Basic spelling mistake. Line 7 says "...trigger task recieves the message". Should be "receives".

Avram Aelony  May 12, 2025 
PDF Page 37
source code

let outome = task_handle.await.unwrap();
println!("Task complete with outcome:{}", outome);

outome => outcome

let outcome = task_handle.await.unwrap();
println!("Task complete with outcome:{}", outcome);

Gioh Kim  May 13, 2025 
Printed Page 193 and 194, Chapter 9 "The Retry Pattern"
function do_something() code

Return type of function do_something() was probably meant to be "Result<(String), [...]>" instead of "Result<(), [...]>". The final line of the function should probably be "Ok(result)" instead of "Ok(())". Variable "miliseconds" should be spelled as "milliseconds".

David Wallace Croft  Jun 29, 2025 
Printed Page 235 of Ch. 11, Sec. "Fine-Grained Future Testing"
2nd code block

If you remark out the "drop(future1);" line, the unit test still passes. This suggests that the theory behind this example might be wrong. If you remark out the preceding or following lines, the unit test will fail which suggest there is something specific about the order of polling and sleeping that releases the lock instead of the drop. I was able to observe being able to remark out the drop line and still have the unit test pass in both my adaption of the code and using a clone of the author's example code on GitHub.

David Wallace Croft  Jul 14, 2025