@@ -944,7 +944,7 @@ impl Worker {
944
944
945
945
println!("Worker {} got a job; executing.", id);
946
946
947
- (* job) ();
947
+ job();
948
948
}
949
949
});
950
950
@@ -976,109 +976,6 @@ The call to `recv` blocks, so if there is no job yet, the current thread will
976
976
wait until a job becomes available. The ` Mutex<T> ` ensures that only one
977
977
` Worker ` thread at a time is trying to request a job.
978
978
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.
1082
979
1083
980
With the implementation of this trick, our thread pool is in a working state!
1084
981
Give it a ` cargo run ` and make some requests:
@@ -1136,7 +1033,7 @@ thread run them.
1136
1033
> limitation is not caused by our web server.
1137
1034
1138
1035
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 .
1140
1037
1141
1038
<span class =" filename " >Filename: src/lib.rs</span >
1142
1039
@@ -1149,7 +1046,7 @@ impl Worker {
1149
1046
while let Ok(job) = receiver.lock().unwrap().recv() {
1150
1047
println!("Worker {} got a job; executing.", id);
1151
1048
1152
- job.call_box ();
1049
+ job();
1153
1050
}
1154
1051
});
1155
1052
@@ -1161,7 +1058,7 @@ impl Worker {
1161
1058
}
1162
1059
```
1163
1060
1164
- <span class =" caption " >Listing 20-22 : An alternative implementation of
1061
+ <span class =" caption " >Listing 20-21 : An alternative implementation of
1165
1062
` Worker::new ` using ` while let ` </span >
1166
1063
1167
1064
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
1175
1072
than intended if we don’t think carefully about the lifetime of the
1176
1073
` MutexGuard<T> ` . Because the values in the ` while ` expression remain in scope
1177
1074
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.
1179
1076
1180
1077
By using ` loop ` instead and acquiring the lock and a job within the block
1181
1078
rather than outside it, the ` MutexGuard ` returned from the ` lock ` method is
1182
1079
dropped as soon as the ` let job ` statement ends. This ensures that the lock is
1183
1080
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.
1185
1082
1186
1083
[ creating-type-synonyms-with-type-aliases] :
1187
1084
ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases
0 commit comments