简体   繁体   English

是否可以在 Rust 中有一个包含不同结构的二进制堆?

[英]Is it possible to have a binary heap containing different structs in Rust?

I'm working on an event-driven particle simulation.我正在研究事件驱动的粒子模拟。 The code below defines the struct Event and implements the equality traits that are needed for a binary heap.下面的代码定义了 struct Event 并实现了二进制堆所需的相等特征。 This code is working but the object event built by the second constructor (new_event) has redundant member variables.此代码有效,但由第二个构造函数 (new_event) 构建的 object 事件具有冗余成员变量。 I want to have a binary heap which works with two different event structs in order to avoid this redundancy and improve my code efficiency which is crucial for my project.我想要一个二进制堆,它与两个不同的事件结构一起工作,以避免这种冗余并提高我的代码效率,这对我的项目至关重要。 Can I achieve this using traits?我可以使用特征来实现这一点吗?

pub struct Event
{
    pub time: f64,
    pub event_type: EventType,
    pub particle_index1: usize,
    pub particle_index2: usize,
    pub particle_count1: usize,
    pub particle_count2: usize
}

impl Event
{
    pub fn new_collision(time: f64, particle_index1: usize, particle_index2: usize, 
        particle_count1: usize, particle_count2: usize) -> Self
    {

        Self {
            time,
            particle_index1,
            particle_index2,
            event_type: EventType::EventCollision,
            particle_count1,
            particle_count2
        }
    }

    pub fn new_event(time: f64, event_type: EventType, particle_index1: usize, particle_count1: 
        usize) -> Self
    {
        Self {
            time,
            particle_index1,
            particle_index2: 0, //REDUNDANT
            event_type,
            particle_count1,
            particle_count2: 0  //REDUNDANT
        }
    }
}

impl PartialEq for Event
{
    fn eq(&self, other: &Self) -> bool
    {
        self.time == other.time
    }
}

impl Eq for Event{}

impl PartialOrd for Event
{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering>
    {
        other.time.partial_cmp(&self.time) //reverse order
    }
}

impl Ord for Event
{
    fn cmp(&self, other: &Self) -> Ordering
    {
        self.partial_cmp(other).unwrap()
    }
}

In this case it would be interesting to use an enum and a combination with a custom trait to avoid cumbersome access to inner enum structs:在这种情况下,使用枚举和自定义特征的组合来避免对内部枚举结构的繁琐访问会很有趣:


use core::cmp::Ordering;
type EventType = String;

trait Timed {
    fn time(&self) -> f64;
}

pub struct CollisionEvent {
    pub time: f64,
    pub particle_index1: usize,
    pub particle_index2: usize,
    pub particle_count1: usize,
    pub particle_count2: usize,
}

pub struct GenericEvent {
    pub time: f64,
    pub event_type: EventType,
    pub particle_index1: usize,
    pub particle_count1: usize,
}

pub enum Event {
    Collision(CollisionEvent),
    Generic(GenericEvent),
}

impl Timed for CollisionEvent {
    fn time(&self) -> f64 {
        self.time
    }
}

impl Timed for GenericEvent {
    fn time(&self) -> f64 {
        self.time
    }
}

impl Timed for Event {
    fn time(&self) -> f64 {
        let event: &dyn Timed = match self {
            Event::Collision(event) => event,
            Event::Generic(event) => event,
        };
        event.time()
    }
}

impl Event {
    pub fn new_collision(
        time: f64,
        particle_index1: usize,
        particle_index2: usize,
        particle_count1: usize,
        particle_count2: usize,
    ) -> Self {
        Self::Collision(CollisionEvent {
            time,
            particle_index1,
            particle_index2,
            particle_count1,
            particle_count2,
        })
    }

    pub fn new_event(
        time: f64,
        event_type: EventType,
        particle_index1: usize,
        particle_count1: usize,
    ) -> Self {
        Self::Generic(GenericEvent {
            time,
            particle_index1,
            event_type,
            particle_count1,
        })
    }
}

impl PartialEq for Event {
    fn eq(&self, other: &Self) -> bool {
        self.time() == other.time()
    }
}

impl Eq for Event {}

impl PartialOrd for Event {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        other.time().partial_cmp(&self.time()) //reverse order
    }
}

impl Ord for Event {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

Playground 操场

It is possible with enums to have a create a data type which can represent multiple "types". 枚举可以创建一个可以表示多个“类型”的数据类型。

So you could do something like:因此,您可以执行以下操作:

pub struct Event {
    pub time: f64,
    pub event_type: EventType,
    pub particle_index1: usize,
    pub particle_count1: usize,
}

pub struct Collison {
    pub time: f64,
    pub event_type: EventType,
    pub particle_index1: usize,
    pub particle_index2: usize,
    pub particle_count1: usize,
    pub particle_count2: usize
}

pub enum EventOrCollision {
    Event(Event),
    Collision(Collision)
}

So you could now have a BinaryHeap<EventOrCollision>> .所以你现在可以有一个BinaryHeap<EventOrCollision>> However the EventOrCollision would have to implement PartialOrd and the other type constraints on BinaryHeap .但是EventOrCollision必须在BinaryHeap上实现PartialOrd和其他类型约束。 You would need to use pattern matching around this type.您需要围绕此类型使用模式匹配

However Rust enums are constant width/size (+1 for the discriminant bit) so the binary heap would take up the same space in memory as you have currently.但是 Rust 枚举是恒定的宽度/大小(判别位为 +1),因此二进制堆将在 memory 中占用与您当前相同的空间。 You would avoid initializing the extra usize fields although that is incredible cheap.您将避免初始化额外的 usize 字段,尽管这非常便宜。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM