Skip to content

Commit ec726b7

Browse files
committed
Remove FnBox and use builtin impl FnOnce for Box<FnOnce()> instead.
1 parent b93ec30 commit ec726b7

File tree

3 files changed

+8
-122
lines changed

3 files changed

+8
-122
lines changed

ci/dictionary.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ Filesystem
160160
filesystem's
161161
filesystems
162162
Firefox
163-
FnBox
164163
FnMut
165164
FnOnce
166165
formatter

src/ch20-02-multithreaded.md

Lines changed: 6 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ impl Worker {
944944
945945
println!("Worker {} got a job; executing.", id);
946946
947-
(*job)();
947+
job();
948948
}
949949
});
950950
@@ -976,109 +976,6 @@ The call to `recv` blocks, so if there is no job yet, the current thread will
976976
wait until a job becomes available. The `Mutex<T>` ensures that only one
977977
`Worker` thread at a time is trying to request a job.
978978

979-
Theoretically, this code should compile. Unfortunately, the Rust compiler isn’t
980-
perfect yet, and we get this error:
981-
982-
```text
983-
error[E0161]: cannot move a value of type std::ops::FnOnce() +
984-
std::marker::Send: the size of std::ops::FnOnce() + std::marker::Send cannot be
985-
statically determined
986-
--> src/lib.rs:63:17
987-
|
988-
63 | (*job)();
989-
| ^^^^^^
990-
```
991-
992-
This error is fairly cryptic because the problem is fairly cryptic. To call a
993-
`FnOnce` closure that is stored in a `Box<T>` (which is what our `Job` type
994-
alias is), the closure needs to move itself *out* of the `Box<T>` because the
995-
closure takes ownership of `self` when we call it. In general, Rust doesn’t
996-
allow us to move a value out of a `Box<T>` because Rust doesn’t know how big
997-
the value inside the `Box<T>` will be: recall in Chapter 15 that we used
998-
`Box<T>` precisely because we had something of an unknown size that we wanted
999-
to store in a `Box<T>` to get a value of a known size.
1000-
1001-
As you saw in Listing 17-15, we can write methods that use the syntax `self:
1002-
Box<Self>`, which allows the method to take ownership of a `Self` value stored
1003-
in a `Box<T>`. That’s exactly what we want to do here, but unfortunately Rust
1004-
won’t let us: the part of Rust that implements behavior when a closure is
1005-
called isn’t implemented using `self: Box<Self>`. So Rust doesn’t yet
1006-
understand that it could use `self: Box<Self>` in this situation to take
1007-
ownership of the closure and move the closure out of the `Box<T>`.
1008-
1009-
Rust is still a work in progress with places where the compiler could be
1010-
improved, but in the future, the code in Listing 20-20 should work just fine.
1011-
People just like you are working to fix this and other issues! After you’ve
1012-
finished this book, we would love for you to join in.
1013-
1014-
But for now, let’s work around this problem using a handy trick. We can tell
1015-
Rust explicitly that in this case we can take ownership of the value inside the
1016-
`Box<T>` using `self: Box<Self>`; then, once we have ownership of the closure,
1017-
we can call it. This involves defining a new trait `FnBox` with the method
1018-
`call_box` that will use `self: Box<Self>` in its signature, defining `FnBox`
1019-
for any type that implements `FnOnce()`, changing our type alias to use the new
1020-
trait, and changing `Worker` to use the `call_box` method. These changes are
1021-
shown in Listing 20-21.
1022-
1023-
<span class="filename">Filename: src/lib.rs</span>
1024-
1025-
```rust,ignore
1026-
trait FnBox {
1027-
fn call_box(self: Box<Self>);
1028-
}
1029-
1030-
impl<F: FnOnce()> FnBox for F {
1031-
fn call_box(self: Box<F>) {
1032-
(*self)()
1033-
}
1034-
}
1035-
1036-
type Job = Box<dyn FnBox + Send + 'static>;
1037-
1038-
// --snip--
1039-
1040-
impl Worker {
1041-
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
1042-
let thread = thread::spawn(move || {
1043-
loop {
1044-
let job = receiver.lock().unwrap().recv().unwrap();
1045-
1046-
println!("Worker {} got a job; executing.", id);
1047-
1048-
job.call_box();
1049-
}
1050-
});
1051-
1052-
Worker {
1053-
id,
1054-
thread,
1055-
}
1056-
}
1057-
}
1058-
```
1059-
1060-
<span class="caption">Listing 20-21: Adding a new trait `FnBox` to work around
1061-
the current limitations of `Box<FnOnce()>`</span>
1062-
1063-
First, we create a new trait named `FnBox`. This trait has the one method
1064-
`call_box`, which is similar to the `call` methods on the other `Fn*` traits
1065-
except that it takes `self: Box<Self>` to take ownership of `self` and move the
1066-
value out of the `Box<T>`.
1067-
1068-
Next, we implement the `FnBox` trait for any type `F` that implements the
1069-
`FnOnce()` trait. Effectively, this means that any `FnOnce()` closures can use
1070-
our `call_box` method. The implementation of `call_box` uses `(*self)()` to
1071-
move the closure out of the `Box<T>` and call the closure.
1072-
1073-
We now need our `Job` type alias to be a `Box` of anything that implements our
1074-
new trait `FnBox`. This will allow us to use `call_box` in `Worker` when we get
1075-
a `Job` value instead of invoking the closure directly. Implementing the
1076-
`FnBox` trait for any `FnOnce()` closure means we don’t have to change anything
1077-
about the actual values we’re sending down the channel. Now Rust is able to
1078-
recognize that what we want to do is fine.
1079-
1080-
This trick is very sneaky and complicated. Don’t worry if it doesn’t make
1081-
perfect sense; someday, it will be completely unnecessary.
1082979

1083980
With the implementation of this trick, our thread pool is in a working state!
1084981
Give it a `cargo run` and make some requests:
@@ -1136,7 +1033,7 @@ thread run them.
11361033
> limitation is not caused by our web server.
11371034
11381035
After learning about the `while let` loop in Chapter 18, you might be wondering
1139-
why we didn’t write the worker thread code as shown in Listing 20-22.
1036+
why we didn’t write the worker thread code as shown in Listing 20-21.
11401037

11411038
<span class="filename">Filename: src/lib.rs</span>
11421039

@@ -1149,7 +1046,7 @@ impl Worker {
11491046
while let Ok(job) = receiver.lock().unwrap().recv() {
11501047
println!("Worker {} got a job; executing.", id);
11511048
1152-
job.call_box();
1049+
job();
11531050
}
11541051
});
11551052
@@ -1161,7 +1058,7 @@ impl Worker {
11611058
}
11621059
```
11631060

1164-
<span class="caption">Listing 20-22: An alternative implementation of
1061+
<span class="caption">Listing 20-21: An alternative implementation of
11651062
`Worker::new` using `while let`</span>
11661063

11671064
This code compiles and runs but doesn’t result in the desired threading
@@ -1175,13 +1072,13 @@ lock. But this implementation can also result in the lock being held longer
11751072
than intended if we don’t think carefully about the lifetime of the
11761073
`MutexGuard<T>`. Because the values in the `while` expression remain in scope
11771074
for the duration of the block, the lock remains held for the duration of the
1178-
call to `job.call_box()`, meaning other workers cannot receive jobs.
1075+
call to `job()`, meaning other workers cannot receive jobs.
11791076

11801077
By using `loop` instead and acquiring the lock and a job within the block
11811078
rather than outside it, the `MutexGuard` returned from the `lock` method is
11821079
dropped as soon as the `let job` statement ends. This ensures that the lock is
11831080
held during the call to `recv`, but it is released before the call to
1184-
`job.call_box()`, allowing multiple requests to be serviced concurrently.
1081+
`job()`, allowing multiple requests to be serviced concurrently.
11851082

11861083
[creating-type-synonyms-with-type-aliases]:
11871084
ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases

src/ch20-03-graceful-shutdown-and-cleanup.md

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -450,17 +450,7 @@ pub struct ThreadPool {
450450
sender: mpsc::Sender<Message>,
451451
}
452452

453-
trait FnBox {
454-
fn call_box(self: Box<Self>);
455-
}
456-
457-
impl<F: FnOnce()> FnBox for F {
458-
fn call_box(self: Box<F>) {
459-
(*self)()
460-
}
461-
}
462-
463-
type Job = Box<dyn FnBox + Send + 'static>;
453+
type Job = Box<dyn FnOnce + Send + 'static>;
464454

465455
impl ThreadPool {
466456
/// Create a new ThreadPool.
@@ -536,7 +526,7 @@ impl Worker {
536526
Message::NewJob(job) => {
537527
println!("Worker {} got a job; executing.", id);
538528

539-
job.call_box();
529+
job();
540530
},
541531
Message::Terminate => {
542532
println!("Worker {} was told to terminate.", id);

0 commit comments

Comments
 (0)