我用 Rust 编写了一个程序,使用步进电机播放音乐,现在我想添加一些假对象,以便我可以进行自动化测试。但是,我不知道有什么好方法来定义这些假对象,以便我的程序可以实际使用它们。
You can .
有效的部分
该程序的主循环使用两个特征对象。一个对象实现了一种称为Timer
,它代表一个计时器,可用于使事情定期发生。另一个对象实现了一个称为Motor
,代表步进电机本身。这些特征的定义位于.
主循环只需等待 500 微秒,将“step”引脚拉高,再等待 500 微秒,将“step”引脚拉低,并重复总共 1,000 个周期。这会导致电机每秒步进 1,000 次,持续 1 秒。
fn spin<T: Timer, M: Motor>(timer: &mut T, motor: &mut M) {
timer.reset();
for _ in 0..1000 {
timer.wait_microseconds(500);
motor.set_step_high();
timer.wait_microseconds(500);
motor.set_step_low();
}
}
到目前为止,一切都很好。我(在我的真实代码中,而不是 Rust Playground 帖子中)有一个有效的实现Timer
,以及一个有效的实施Motor
,以及spin
函数使电机旋转,声音优美。
有问题的部分
我希望能够进行自动化测试,所以我编写了一个“假”对象来实现Motor
and Timer
以一种对测试有用的方式。类型本身只是一个结构体:
/// A mock timer and motor which simply tracks the amount of simulated time that
/// the motor driver has its "step" pin pulled HIGH.
struct DummyTimerMotor {
is_on: bool,
time_high: u64,
}
的实施set_step_high
and set_step_low
刚刚设置is_on
to true
and false
(分别),以及实施wait_microseconds
只是检查是否is_on
is true
并将给定的时间添加到time_high
如果是这样。这些实现位于.
天真地,我希望能够通过DummyTimerMotor
对象作为两个参数spin
,然后看看time_high
然后,看到它的值为 500000。但是,这当然是不允许的:
fn main() {
let mut dummy: DummyTimerMotor = DummyTimerMotor {
is_on: false, time_high: 0
};
spin(&mut dummy, &mut dummy); // Oops, not allowed!
print!("The 'step' pin was HIGH for {} microseconds", dummy.time_high);
}
这会给出错误消息:“无法借用dummy
一次不止一次地可变。”
我确切地知道为什么会收到该错误消息,而且这是有道理的。获得我想要的行为的好方法是什么?
我只有一个合理的想法:改变spin
这样就不用采用实现的对象Timer
,以及另一个实现的对象Motor
,它需要一个实现两者的单个对象Timer
and Motor
。然而,这对我(作为 Rust 新手)来说似乎不太优雅。从概念上讲,定时器是一回事,而电机是另一回事;拥有spin
采用一个既是定时器又是电机的对象是相当不直观的。看来我不应该改变这种方式spin
实现只是为了适应定时器和电机如何实现的细节。
完整代码清单
万一 Rust Playground 出现故障,下面是我在那里的整个程序,以及整个错误输出。
/// A timer which can be used to make things happen at regular intervals.
trait Timer {
/// Set the timer's reference time to the current time.
fn reset(&mut self);
/// Advance the timer's reference time by the given number of microseconds,
/// then wait until the reference time.
fn wait_microseconds(&mut self, duration: u64);
}
/// The interface to a stepper motor driver.
trait Motor {
/// Pull the "step" pin HIGH, thereby asking the motor driver to move the
/// motor by one step.
fn set_step_high(&mut self);
/// Pull the "step" pin LOW, in preparation for pulling it HIGH again.
fn set_step_low(&mut self);
}
fn spin<T: Timer, M: Motor>(timer: &mut T, motor: &mut M) {
timer.reset();
for _ in 0..1000 {
timer.wait_microseconds(500);
motor.set_step_high();
timer.wait_microseconds(500);
motor.set_step_low();
}
}
/// A mock timer and motor which simply tracks the amount of simulated time that
/// the motor driver has its "step" pin pulled HIGH.
struct DummyTimerMotor {
is_on: bool,
time_high: u64,
}
impl Timer for DummyTimerMotor {
fn reset(&mut self) { }
fn wait_microseconds(&mut self, duration: u64) {
if self.is_on {
self.time_high += duration;
}
}
}
impl Motor for DummyTimerMotor {
fn set_step_high(&mut self) {
self.is_on = true;
}
fn set_step_low(&mut self) {
self.is_on = false;
}
}
fn main() {
let mut dummy: DummyTimerMotor = DummyTimerMotor {
is_on: false, time_high: 0
};
spin(&mut dummy, &mut dummy); // Oops, not allowed!
print!("The 'step' pin was HIGH for {} microseconds", dummy.time_high);
}
error[E0499]: cannot borrow `dummy` as mutable more than once at a time
--> src/main.rs:61:22
|
61 | spin(&mut dummy, &mut dummy); // Oops, not allowed!
| ---- ---------- ^^^^^^^^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call