当一个对象可能实现多个特征时,如何创建对特征的引用结构?
How do I create a struct of references to traits when one object might implement multiple of the traits?
我有一个管理多个传感器的结构。我有陀螺仪、加速度计、磁力计、气压计和温度计。都是特质。
pub struct SensorManager {
barometer: Barometer + Sized,
thermometer: Thermometer + Sized,
gyroscope: Gyroscope + Sized,
accelerometer: Accelerometer + Sized,
magnetometer: Magnetometer + Sized
}
我需要将其模块化,以便在配置文件中指定您使用的传感器。
问题是一些传感器重叠了。例如:一个人可以拥有一个包含陀螺仪、加速度计和磁力计的LSM9DS0,而另一个人可以拥有一个L3GD20陀螺仪和一个LSM303D加速度计、磁力计。
在 C++ 中,我会存储指针或引用,但我不确定如何在 Rust 中安全地正确实现它。
简短版本:需要将每个传感器作为该结构的成员进行引用。其中一些引用属于同一对象。
In C++ I would store pointers or references
Rust 并不是那么陌生。你做同样的事情。主要区别在于 Rust 阻止您通过两条不同的路径改变一件事或有一个悬垂的引用。
回答您的问题有 许多 个可能的解决方案。例如,您没有描述是否需要能够改变传感器或描述传感器是否会比管理器寿命更长,是否会涉及线程等。所有这些都会影响代码的微优化程度.
最灵活的解决方案是:
使用shared ownership, such as that provided by Rc
or Arc
。这允许多个事物拥有传感器。
使用interior mutability, such as that provided by RefCell
or Mutex
。这将一次执行单个变异引用从编译时间移动到 运行 时间。
使用 trait objects 来模拟动态调度,因为在 运行 时间决定使用什么具体对象。
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,
你真的不想这样做。 Barometer
既是 trait 又是 type,但类型没有大小。它总是需要在指针后面引用(&Barometer
、Box<Barometer>
、RefCell<Barometer>
等)
另请参阅:
我有一个管理多个传感器的结构。我有陀螺仪、加速度计、磁力计、气压计和温度计。都是特质。
pub struct SensorManager {
barometer: Barometer + Sized,
thermometer: Thermometer + Sized,
gyroscope: Gyroscope + Sized,
accelerometer: Accelerometer + Sized,
magnetometer: Magnetometer + Sized
}
我需要将其模块化,以便在配置文件中指定您使用的传感器。
问题是一些传感器重叠了。例如:一个人可以拥有一个包含陀螺仪、加速度计和磁力计的LSM9DS0,而另一个人可以拥有一个L3GD20陀螺仪和一个LSM303D加速度计、磁力计。
在 C++ 中,我会存储指针或引用,但我不确定如何在 Rust 中安全地正确实现它。
简短版本:需要将每个传感器作为该结构的成员进行引用。其中一些引用属于同一对象。
In C++ I would store pointers or references
Rust 并不是那么陌生。你做同样的事情。主要区别在于 Rust 阻止您通过两条不同的路径改变一件事或有一个悬垂的引用。
回答您的问题有 许多 个可能的解决方案。例如,您没有描述是否需要能够改变传感器或描述传感器是否会比管理器寿命更长,是否会涉及线程等。所有这些都会影响代码的微优化程度.
最灵活的解决方案是:
使用shared ownership, such as that provided by
Rc
orArc
。这允许多个事物拥有传感器。使用interior mutability, such as that provided by
RefCell
orMutex
。这将一次执行单个变异引用从编译时间移动到 运行 时间。使用 trait objects 来模拟动态调度,因为在 运行 时间决定使用什么具体对象。
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,
你真的不想这样做。 Barometer
既是 trait 又是 type,但类型没有大小。它总是需要在指针后面引用(&Barometer
、Box<Barometer>
、RefCell<Barometer>
等)
另请参阅: