如果我想将单个可变对象传递给函数的多个参数,我该怎么办? 起作用的部分有问题的部分完整代码清单

问题描述

我已经用 Rust 编写了一个使用步进电机播放音乐的程序,现在我想添加一些假对象,以便我可以进行自动化测试。但是,我不知道以我的程序可以实际使用它们的方式定义这些假对象的好方法。

你可以run my code on the Rust Playground

起作用的部分

程序的主循环使用了两个特征对象。一个对象实现了一个名为 Timer 的特征,它表示一个计时器,可用于使事情定期发生。另一个对象实现了一个名为 Motor 的特征,并代表步进电机本身。这些特征的定义在 my Rust Playground post 上。

主循环简单地等待 500 微秒,将“step”引脚拉高,再等待 500 微秒,将“step”引脚拉低,并重复总共 1,000 个周期。这会导致电机每秒步进 1,000 次,持续一秒。

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 函数使电机旋转,它听起来很美。

有问题的部分

我希望能够进行自动化测试,因此我编写了一个“假”对象,它以一种对测试有用的方式实现了 MotorTimer。类型本身只是一个结构体:

/// 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_highset_step_low 的实现只是将 is_on 设置为 truefalse(分别),而 wait_microseconds 的实现只是检查 is_on 是否为 true,如果是,则将给定的时间添加到 time_high。这些实现在 my Rust Playground post 中。

天真地,我希望能够将 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 的对象,而是采用一个同时实现两者的对象TimerMotor。但是,这对我来说似乎不雅(作为 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,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,}

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,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,not allowed!
   |     ---- ----------  ^^^^^^^^^^ second mutable borrow occurs here
   |     |    |
   |     |    first mutable borrow occurs here
   |     first borrow later used by call

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)