rust 'self'是'FnMut'闭包中的捕获变量

6kkfgxo0  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(137)

我试图创建一个结构体,使能一个引脚,然后在一段时间后禁用它。我遇到了一个所有权问题,我不太确定如何解决它。
下面是我正在使用的代码:

  1. pub trait PumpPin {
  2. fn enable(self);
  3. fn disable(self);
  4. }
  5. impl PumpPin for Gpio4<Output> {
  6. fn enable(mut self: Gpio4<Output>) {
  7. self.set_high().unwrap();
  8. }
  9. fn disable(mut self: Gpio4<Output>) {
  10. self.set_low().unwrap();
  11. }
  12. }
  13. pub struct PumpManager<T: PumpPin + std::marker::Send + 'static + std::marker::Sync> {
  14. pin: T,
  15. }
  16. impl<T: PumpPin + std::marker::Send + 'static + std::marker::Sync> PumpManager<T> {
  17. pub fn new(pin: T) -> PumpManager<T> {
  18. PumpManager { pin }
  19. }
  20. pub fn run_pump(self, time: u64) -> Result<(), EspError> {
  21. self.pin.enable();
  22. // Configure the timer to disable the pin
  23. let once_timer = EspTimerService::new()
  24. .unwrap()
  25. .timer(move || self.pin.disable());
  26. //actually set a time for the timer
  27. once_timer.unwrap().after(Duration::from_secs(time))
  28. }
  29. }

我的目标是封装所有处理泵定时的逻辑。
下面的错误位于self.pin.disable()

  1. cannot move out of `self.pin`, as `self` is a captured variable in an `FnMut` closure
  2. move occurs because `self.pin` has type `T`, which does not implement the `Copy` trait

我以为pin的所有权属于PumpManager本身,但实际上它属于调用它的任何函数。有没有方法可以重构它来完成我想做的事情?

wr98u20j

wr98u20j1#

你的直觉几乎是正确的:pinPumpManager拥有,但是PumpManagerrun_pump()拥有,因为该方法通过self取得所有权。
这里有一个共同的期望:PumpPin::disable()通过self获得所有权,但是esp_idf_svc::timer::EspTimerService::timer()需要FnMut(),也就是说,它需要可以多次调用的闭包但是我们不能把self.pin的所有权移入闭包,通过pin.disable()pin移出闭包,并再次调用闭包-因为pin到那时已经不存在了。这就是为什么FnMut的需求没有得到满足的原因。

  • 本质上,您的问题是EspTimerService::timer()强制一个闭包,它可以从周期性计时器调用,而PumpPin::disable()强制它只能被调用一次,但它可以从一个周期性计时器调用;有人可能会说esp_idf_svc-API的设计很糟糕,但事实就是如此。*

如果你可以将PumpPin::disable()更改为不获取所有权(但是,例如&mut self),那么你的闭包就可以变成FnMut
如果你不能改变PumpPin::disable(),你可以把self.pin移到一个Option中,把.take()移到闭包中那个Option之外的pin中。(计时器执行的唯一时间),如果计时器多次触发(虽然不会发生,但就编译器而言,它是正确的),则会在此后返回None。例如:

  1. // Some FnMut, like the timer-callback
  2. fn take<T: FnMut()>(_: T) {}
  3. struct Foo {
  4. bar: String,
  5. }
  6. impl Foo {
  7. fn do_take(self) {
  8. // Destructure `self`, put `bar` into an `Option`
  9. let mut s = Some(self.bar);
  10. take(move || {
  11. // Move out of the `Option`.
  12. // If this closure was executed before (as a `FnMut` could),
  13. // this would not execute, because we leave a `None`
  14. if let Some(s) = s.take() {
  15. // We now own `s` and can call methods that take `self`.
  16. // But this path can only execute once, while the closure
  17. // as a whole can execute multiple times.
  18. println!("{s}");
  19. }
  20. })
  21. }
  22. }
展开查看全部

相关问题