Mock methods can't return by move
simulacrum_mock::method::TrackedMethod::returning takes an FnMut closure, from which captured variables cannot be moved. Captured variables can only be moved out of FnOnce closures. This is a problem, because it makes it impossible to return a non-constant, non-Clone value from an expectation.
Sample code:
/// A handy type that is non-Clone and non-Copy
#[derive(Debug, Eq, PartialEq)]
pub struct UniquelyOwned(u32);
#[test]
fn return_owned() {
pub trait A {
fn foo(&self) -> UniquelyOwned;
}
create_mock! {
impl A for AMock (self) {
expect_foo("foo"):
fn foo(&self) -> UniquelyOwned;
}
}
let mut mock = AMock::new();
let res = UniquelyOwned(42);
mock.expect_foo().called_once().returning(move |_| res);
assert_eq!(UniquelyOwned(42), mock.foo());
}
And the compile error:
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> src/t_simulacrum.rs:521:60
|
520 | let res = UniquelyOwned(42);
| --- captured outer variable
521 | mock.expect_foo().called_once().returning(move |_| res);
| ^^^ cannot move out of captured outer variable in an `FnMut` closure
error: aborting due to previous error
For more information about this error, try `rustc --explain E0507`.
Mock_Derive's solution is to store the result uniquely in the expectation, in an Option or something. It is then a runtime error to call that method twice without putting another return value into it. For expectations that need to be called multiple times, then must compute their return values by means of a closure instead.
Mockers' solution is similar.
BTW, I've been using a workaround that looks like this. It's basically what mockers and Mock_Derive do internally.
let mut mock = AMock::new();
let res = Some(UniquelyOwned(42));
mock.expect_foo().called_once().returning(move |_| res.take().unwrap());