如果我想将单个可变对象传递给函数的多个参数,我该怎么办?

2024-04-13

我用 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

你可以拥有与众不同的DummyTimer and DummyMotor谁通过共享状态Rc<RefCell<State>>:

struct State {
    is_on: bool,
    time_high: u64,
}

struct DummyTimer {
    state: Rc<RefCell<State>>,
}

impl Timer for DummyTimer {
    fn reset(&mut self) { }
    
    fn wait_microseconds(&mut self, duration: u64) {
        let mut t = self.state.borrow_mut();
        if t.is_on {
            t.time_high += duration;
        }
    }
}

struct DummyMotor {
    state: Rc<RefCell<State>>,
}

impl Motor for DummyMotor {
    fn set_step_high(&mut self) {
        self.state.borrow_mut().is_on = true;
    }
    
    fn set_step_low(&mut self) {
        self.state.borrow_mut().is_on = false;
    }
}

fn main() {
    let state = Rc::new (RefCell::new (State { is_on: false, time_high: 0, }));
    let mut motor = DummyMotor { state: Rc::clone (&state), };
    let mut timer = DummyTimer { state: Rc::clone (&state), };

    spin(&mut timer, &mut motor); // Now it's allowed
    
    print!("The 'step' pin was HIGH for {} microseconds", state.borrow().time_high);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果我想将单个可变对象传递给函数的多个参数,我该怎么办? 的相关文章

随机推荐