How do I create a struct of references to traits when one object might implement multiple of the traits?

╄→гoц情女王★ 提交于 2020-05-31 04:13:12

问题


I have a struct which manages several sensors. I have a gyroscope, accelerometer, magnetometer, barometer, and thermometer. All of which are traits.

pub struct SensorManager {
    barometer: Barometer + Sized,
    thermometer: Thermometer + Sized,
    gyroscope: Gyroscope + Sized,
    accelerometer: Accelerometer + Sized,
    magnetometer: Magnetometer + Sized
}

I need to make it modular so in the configuration file you can specify which sensors you are using.

The problem is that some of the sensors overlap. For example: one person can have an LSM9DS0 which contains gyroscope, accelerometer, and magnetometer while another person can have an L3GD20 gyroscope and an LSM303D accelerometer, magnetometer.

In C++ I would store pointers or references, but I am not sure how to properly implement this safely in Rust.

Short version: Need to have references to each sensor as members of this struct. Some of these references are of the same object.


回答1:


In C++ I would store pointers or references

Rust isn't that alien. You do the same thing. The main difference is that Rust prevents you from being able to mutate one thing via two different paths or to have a reference that dangles.

Answering your question has many potential solutions. For example, you don't describe whether you need to be able to mutate the sensors or describe if the sensors will outlive the manager, if threads will be involved, etc.. All of these things affect how micro-optimized the code can be.

The maximally-flexible solution is to:

  1. use shared ownership, such as that provided by Rc or Arc. This allows multiple things to own the sensor.

  2. use interior mutability, such as that provided by RefCell or Mutex. This moves enforcement of a single mutating reference at a time from compile time to run time.

  3. use trait objects to model dynamic dispatch since the decision of what concrete objects to use is made at run time.

use std::{cell::RefCell, rc::Rc};

trait Barometer {
    fn get(&self) -> i32;
    fn set(&self, value: i32);
}

trait Thermometer {
    fn get(&self) -> i32;
    fn set(&self, value: i32);
}

trait Gyroscope {
    fn get(&self) -> i32;
    fn set(&self, value: i32);
}

struct Multitudes;
impl Barometer for Multitudes {
    fn get(&self) -> i32 {
        1
    }
    fn set(&self, value: i32) {
        println!("Multitudes barometer set to {}", value)
    }
}

impl Thermometer for Multitudes {
    fn get(&self) -> i32 {
        2
    }
    fn set(&self, value: i32) {
        println!("Multitudes thermometer set to {}", value)
    }
}

struct AutoGyro;

impl Gyroscope for AutoGyro {
    fn get(&self) -> i32 {
        3
    }
    fn set(&self, value: i32) {
        println!("AutoGyro gyroscope set to {}", value)
    }
}

struct SensorManager {
    barometer: Rc<RefCell<dyn Barometer>>,
    thermometer: Rc<RefCell<dyn Thermometer>>,
    gyroscope: Rc<RefCell<dyn Gyroscope>>,
}

impl SensorManager {
    fn new(
        barometer: Rc<RefCell<dyn Barometer>>,
        thermometer: Rc<RefCell<dyn Thermometer>>,
        gyroscope: Rc<RefCell<dyn Gyroscope>>,
    ) -> Self {
        Self {
            barometer,
            thermometer,
            gyroscope,
        }
    }

    fn dump_info(&self) {
        let barometer = self.barometer.borrow();
        let thermometer = self.thermometer.borrow();
        let gyroscope = self.gyroscope.borrow();

        println!(
            "{}, {}, {}",
            barometer.get(),
            thermometer.get(),
            gyroscope.get()
        );
    }

    fn update(&self) {
        self.barometer.borrow_mut().set(42);
        self.thermometer.borrow_mut().set(42);
        self.gyroscope.borrow_mut().set(42);
    }
}

fn main() {
    let multi = Rc::new(RefCell::new(Multitudes));
    let gyro = Rc::new(RefCell::new(AutoGyro));

    let manager = SensorManager::new(multi.clone(), multi, gyro);

    manager.dump_info();
    manager.update();
}

Complete example on the Playground


barometer: Barometer + Sized,

You really don't want to to this. Barometer is both a trait and a type, but the type doesn't have a size. it always needs to be referenced behind a pointer (&Barometer, Box<Barometer>, RefCell<Barometer>, etc.)

See also:

  • What does "dyn" mean in a type?


来源:https://stackoverflow.com/questions/45534394/how-do-i-create-a-struct-of-references-to-traits-when-one-object-might-implement

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!