Lessons from the Implementation
We record the implementation of the ThreadPool in the next section. For this section, we record several encountered errors and methods to get around them.
Box, Rc<T> | Weak<T>, RefCell<T>, Mutex<T>, Arc<t>
-
Box<dyn T>
- Single thread only
- Help save data on heap instead of memory
- Checked in compile time
-
Rc<T> | Weak<T>
- Reference Count Type
- Checked in compile time
- Used in single thread only
- Created by
let a = Rc:new(data)
- Cloned by
Rc::clone(&a)
a
above is immutable- Used when multiple reference is needed
- Mainly used as immutable reference
- Combined with
RefCell<T>
, we can implement interior mutability Rc<T>.downgrade()
andRc<T>.upgrade()
can switch a reference to weak and strong respectively- (cont'd) Used when there is a recursive relation that has parent-children relationship
weak_reference_count
wouldn't affect the release of resource as long as thestrong_reference_count
of a variable goes to0
-
RefCell<T>
-
Reference Cell Type
-
Only checked in runtime
-
Used in single thread only
-
Can be used in mutable and immutable reference
-
Let
let a = Rc::new(RefCell(4))
, thena
is immutable -
But
a.borrow_mut()
can create a mutable reference -
(cont'd) After destructured,
*a.borrow_mut() += 10
becomes valid -
Example (Interior Mutability).
// a should have been immutable, let a = Rc::new(RefCell::new(123)); // but with RefCell::new, the data become mutable by using .borrow_mut() let b = Rc::new(&a); *b.borrow_mut() += 1000; print!("{:?}, {:?}", a, b); // print: // RefCell { value: 1123 }, RefCell { value: 1123 }
-
Some comments in the internet suggest avoiding interior mutability when possible
-
An immutable ereference can be created by
a.borrow()
-
-
Mutex<T>
- Used in multi-threaded scenario
- Used when internal state of the object of type
T
can change in different thread, this is to avoid data race - Need
Mutex<T>.lock.unwrap()
to get the lock and gain right to access and mutate the value
-
Arc<T>
- Used in multi-threaded scenario
- Thread-safe and multi-threaded version of
Rc<T>
, can be referenced by many threads - A stands for atomic
- Example. Suppose that a single
receiver: Arc<Mutex<mpsc::Receiver<Message>>>
is passed to 10 threads:
We will be using modulelet message: Message = receiver.lock().unwrap().recv().unwrap();
mpsc
(multi-producer, single-consumer) to getSender
andReceiver
Access Struct's Field but Encounter: does not implement the Copy trait Error
We consider the following exmaple:
struct Foo { bar: Bar, } impl Foo { fn foo(&self) { self.bar.bar(); } } struct Bar {} impl Bar { fn bar(self) {} } fn main() { let foo = Foo { bar: Bar {} }; foo.foo(); }
By cargo check
we get
93 | let bar = self.bar; | ^^^^^^^^ | | | move occurs because `self.bar` has type `Bar`, which does not implement the `Copy` trait | help: consider borrowing here: `&self.bar`
Reason of the Problem
The problem is that fn Bar.bar
takes the ownership of the Bar
instance (the self.bar
). If self.bar.bar()
were executable, then self.bar
will be release accidentally after the execution of that function is finished, which is disastrous and should be forbiddened.
Solution: Universal Trick by using Option<T>
In other words, self.bar
must be taken away intentionally in order to be fed into .bar
method. The universal trick is to make self.bar
be of type Option<T>
, then
(self.bar as Option<T>).take()
can take away both the ownership and the value of self.bar
and set self.bar
to None
:
... struct Foo { bar: Option<Bar>, } impl Foo { fn foo(&mut self) { if let Some(bar) = self.bar.take() { bar.bar(); } } } ...
Note that because self.bar
is mutated (as self.bar
becomes Option::None
), we need foo(&mut self)
instead of foo(&self)
.
Possible Scenario.
The problem discusses above occur occasionally. For example, if our struct contains a field named thread: thread::JoinHandle<()>
, and if we want to execute thread.join()
, then because the signature of .join
is:
pub fn join(self) -> Result<T>
we must take out the thread
intentionally by chaning the type of the field thread
from type thread::JoinHandle<()>
to Option<thread::JoinHandle<()>>
.