@@ -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
976976wait 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
1083980With the implementation of this trick, our thread pool is in a working state!
1084981Give 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
11381035After 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
11671064This 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
11751072than 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
11771074for 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
11801077By using ` loop ` instead and acquiring the lock and a job within the block
11811078rather than outside it, the ` MutexGuard ` returned from the ` lock ` method is
11821079dropped as soon as the ` let job ` statement ends. This ensures that the lock is
11831080held 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] :
11871084ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases
0 commit comments