如何将变量存储在特征中,以便它可以在特征的其他方法中使用?

How can I store variables in traits so that it can be used in other methods on trait?

我的代码中有几个具有共同功能的对象。这些对象本质上就像服务一样,它们有一个生命周期(由 start/stop 控制)并执行工作直到它们的生命周期结束。我正在尝试重构我的代码以减少重复但卡住了。

在高层次上,我的对象都实现了下面的代码。

impl SomeObject {
    fn start(&self) {
        // starts a new thread.
        // stores the 'JoinHandle' so thread can be joined later.
    }

    fn do_work(&self) {
        // perform work in the context of the new thread.
    }

    fn stop(&self) {
        // interrupts the work that this object is doing.
        // stops the thread.
    }
}

本质上,这些对象就像“服务”一样,所以为了重构,我的第一个想法是我应该创建一个名为“服务”的特征,如下所示。

trait Service {
    fn start(&self) {}

    fn do_work(&self);

    fn stop(&self) {}      
}

然后,我可以更新我的对象以实现“服务”特性。我遇到的问题是,由于不允许特征具有 fields/properties,我不确定如何在特征中保存 'JoinHandle' 以便我可以在其他方法。

在 Rust 中是否有惯用的方法来处理这个问题?

tldr;如何将变量保存在特征中,以便它们可以在不同的特征方法中重复使用?

编辑:

这是我确定的解决方案。感谢任何反馈。

extern crate log;

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::JoinHandle;

pub struct Context {
    name: String,
    thread_join_handle: JoinHandle<()>,
    is_thread_running: Arc<AtomicBool>,
}

pub trait Service {
    fn start(&self, name: String) -> Context {
        log::trace!("starting service, name=[{}]", name);
        let is_thread_running = Arc::new(AtomicBool::new(true));
        let cloned_is_thread_running = is_thread_running.clone();
        let thread_join_handle = std::thread::spawn(move || loop {
            while cloned_is_thread_running.load(Ordering::SeqCst) {
                Self::do_work();
            }
        });
        log::trace!("started service, name=[{}]", name);
        return Context {
            name: name,
            thread_join_handle: thread_join_handle,
            is_thread_running: is_thread_running,
        };
    }

    fn stop(context: Context) {
        log::trace!("stopping service, name=[{}]", context.name);
        context.is_thread_running.store(false, Ordering::SeqCst);
        context
            .thread_join_handle
            .join()
            .expect("joining service thread");
        log::trace!("stopped service, name=[{}]", context.name);
    }

    fn do_work();
}

我认为您只需要将 JoinHandle 保存在结构的状态本身中,然后其他方法也可以访问它,因为它们都已经获得了传递给它们的所有结构数据。

struct SomeObject {
  join_handle: JoinHandle;
}

impl Service for SomeObject {
  fn start(&mut self) {
      // starts a new thread.
      // stores the 'JoinHandle' so thread can be joined later.
      self.join_handle = what_ever_you_wanted_to_set //Store it here like this
  }

  fn do_work(&self) {
      // perform work in the context of the new thread.
  }

  fn stop(&self) {
      // interrupts the work that this object is doing.
      // stops the thread.
  }
}

希望对你有用。

根据您使用特征的方式,您可以重组代码并使其开始 returns JoinHandle,其他函数将连接句柄作为输入

trait Service {
    fn start(&self) -> JoinHandle;

    fn do_work(&self, handle: &mut JoinHandle);

    fn stop(&self, handle: JoinHandle);
}

(可能根据您的需要使用不同的函数参数)。通过这种方式,您可以通过将所有处理句柄(哈哈)的代码放在结构本身之外并使其更通用来减少重复代码。如果您希望结构在此特征之外使用 JoinHandle,我会说最好按照 Equinox 的建议进行操作,并将其设为一个字段。