Reputation: 137
Consider my struct WareHouse
which has a watch
function which takes a closure as an argument.
The struct also has an update
function which updates a path and a value, and should then call the closure passed into the watch
function if applicable.
How can I verify that when I call watch(callback)
followed by an update the callback function is triggered?
I tried:
#[test]
fn calls_watcher_on_update() {
let mut w = WareHouse::new();
let mut called = false;
// set a watcher
w.watch(move|| {
println!("watcher called");
called = true;
});
w.update("/foo", "baz").unwrap();
assert!(called);
}
Upvotes: 5
Views: 349
Reputation: 2581
A different way would be to encapsulate the called
status into your Warehouse
struct. If you need that value to follow your struct around as it's passed to functions or something, this will handle that without needing to track multiple variables:
struct Warehouse {
callback: Option<fn() -> ()>,
called: bool,
}
impl Warehouse {
fn new() -> Self {
Self {
callback: None,
called: false,
}
}
fn watch(&mut self, f: fn() -> ()) {
self.callback = Some(f);
}
fn update(&mut self, path: &str, value: &str) -> Result<(), ()> {
if let Some(cb) = self.callback {
cb();
self.called = true;
Ok(())
} else {
Err(())
}
}
fn was_called(&self) -> bool {
self.called
}
}
fn main() {
let mut w = Warehouse::new();
// set a watcher
w.watch(move|| {
println!("watcher called");
});
// updates *and* calls the closure
w.update("/foo", "baz").unwrap();
// getter method to see if it was called (could also be pub member)
assert!(w.was_called());
}
Upvotes: 2
Reputation: 169403
When you try to move a value that implements Copy
, it is copied instead. So your closure captures the value of called
the moment that it was created; called
within and outside of the closure are effectively two different variables.
You should even be getting a warning on this, something like:
warning: value captured by `called` is never read
Instead, consider capturing a Cell<bool>
by reference. I'm inferring that you need a Cell
here because capturing by mutable reference would require that w
drop the closure before you can read the value in assert!()
or you would get a "cannot borrow mutably while already borrowed" error.
use std::cell::Cell;
fn main() {
// Doesn't work right; called is copied into the closure, not moved.
{
let mut called = false;
let mut closure = move || {
called = true;
println!("(A) In closure, called is {called}");
};
closure();
println!("(A) Outside of closure, called is {called}");
}
// Workes correctly; the closure captures a reference to the cell.
{
let called = Cell::new(false);
let closure = || {
called.set(true);
println!("(B) In closure, called is {}", called.get());
};
closure();
println!("(B) Outside of closure, called is {}", called.get());
}
}
This will output:
(A) In closure, called is true
(A) Outside of closure, called is false
(B) In closure, called is true
(B) Outside of closure, called is true
Upvotes: 4