Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serde failing with non-primitive items #4

Closed
ckaran opened this issue Oct 17, 2017 · 5 comments
Closed

serde failing with non-primitive items #4

ckaran opened this issue Oct 17, 2017 · 5 comments
Assignees
Labels

Comments

@ckaran
Copy link

ckaran commented Oct 17, 2017

I've been testing priority-queue for use in a home grown simulator framework. One of the things I need is for the queue and its contents to be serializeable via serde. When I test priority-queue using primitive types for the items, this works, but if I test using non-primitive types for the items, I get a the error string thread 'main' panicked at 'called Result::unwrap() on an Err value: ErrorImpl { code: KeyMustBeAString, line: 0, column: 0 }', src/libcore/result.rs:906:4. The following code should illustrate the problem; look in the area under the FAILING block.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;
extern crate uuid;
extern crate priority_queue;

use priority_queue::PriorityQueue;
use std::cmp::{Ord, PartialOrd, Ordering};
use std::default::Default;
use std::time::Duration;
use uuid::Uuid;

// Abusing Duration as a mutable std::time::Instant
type ActivationDate = Duration;

/// Events are compared by EventComparables instances.
///
/// EventComparables instances are similar to instances of time, but with the
/// extra wrinkle of having a Uuid instance.  When EventComparables instances
/// are compared, they are first compared by their activation date, with the
/// date that occurs earlier being greater than a date that occurs later. This
/// ordering exists because of how priority_queue::PriorityQueue works; it is
/// naturally a max priority queue; using this ordering makes it a min
/// priority queue. EventComparables go one step beyond using time as the key
/// though; they  also have uuid::Uuid instances which are used to guarantee
/// that every EventComparables is unique.  This ensures that if a set of
/// events all  occur at the same time, they will still be executed in a
/// deterministic order, every single time the queue's contents are executed.
/// This is  critical for deterministic simulators.
#[serde(default)]
#[serde(deny_unknown_fields)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
struct EventComparables {
    /// This is when the event will be fired.
    activation_date: ActivationDate,

    /// This is a unique ID.  Except for ensuring that different events are
    /// guaranteed to compare as being different, it has no purpose.
    id: Uuid
}

/// Default events activate at time (0, 0)
///
/// All default events first at time (0, 0), but every single one has a unique
/// id.  This ensures that regardless of the number of default events you
/// create, they will always execute in the same order.
impl Default for EventComparables {
    fn default() -> Self {
        EventComparables {activation_date: ActivationDate::new(0,0), id: Uuid::new_v4()}
    }
}

/// The priority queue depends on `Ord`. Explicitly implement the trait so the
/// queue becomes a min-heap instead of a max-heap.
impl Ord for EventComparables {
    fn cmp(&self, other: &Self) -> Ordering {

        // Notice that the we flip the ordering on costs. In case of a tie we
        // compare by id - this step is necessary to make implementations of
        // `PartialEq` and `Ord` consistent.

        other.activation_date.cmp(&self.activation_date)
            .then_with(|| self.id.cmp(&other.id))
    }
}

// `PartialOrd` needs to be implemented as well.
impl PartialOrd for EventComparables {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

/// A fake event to fire on some date.
///
/// This is a fake event that I'll fire when the corresponding
/// EventComparables instance comes up.  The contents are immaterial; I'm just
/// using it for testing
#[serde(default)]
#[serde(deny_unknown_fields)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
struct ConcreteEvent1 {
    a: i32,
    b: i64
}

impl Default for ConcreteEvent1 {
    fn default() -> Self {
        ConcreteEvent1 {a: 0, b: 0}
    }
}

//////////////////////////////////////////////////////////////////////////////
// Test 1
//////////////////////////////////////////////////////////////////////////////

fn test1() {
    println!("test1()");

    type PqType = PriorityQueue<i32, i32>;

    let mut pq: PqType = PriorityQueue::new();
    pq.push(0, 0);
    pq.push(1, 1);

    let serialized = serde_json::to_string(&pq).unwrap();
    println!("serialized = {:?}", serialized);
    let deserialized: PqType = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
}

//////////////////////////////////////////////////////////////////////////////
// Test 2
//////////////////////////////////////////////////////////////////////////////

fn test2() {
    println!("\n\ntest2()");

    type PqType = PriorityQueue<i32, EventComparables>;

    let mut pq: PqType = PriorityQueue::new();
    pq.push(0, Default::default()); // Uuids will be different
    pq.push(1, Default::default());

    let serialized = serde_json::to_string(&pq).unwrap();
    println!("serialized = {:?}", serialized);
    let deserialized: PqType = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
}

//////////////////////////////////////////////////////////////////////////////
// Test 3
//////////////////////////////////////////////////////////////////////////////

fn test3() {
    println!("\n\ntest3()");

    // Create some concrete events and comparables, and test to see that they
    // can be serialized/deserialized

    let ce1 = ConcreteEvent1{a: 12, b: 45};
    let ec1 = EventComparables {activation_date: ActivationDate::new(0, 1), id: Uuid::new_v4()};

    let ce2 = ConcreteEvent1{a: 37, b: 123};
    let ec2 = EventComparables {activation_date: ActivationDate::new(0, 1), id: Uuid::new_v4()};

    let serialized = serde_json::to_string(&ce1).unwrap();
    println!("serialized = {:?}", serialized);
    let deserialized: ConcreteEvent1 = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
    assert_eq!(ce1, deserialized);

    let serialized = serde_json::to_string(&ec1).unwrap();
    println!("serialized = {:?}", serialized);
    let deserialized: EventComparables = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
    assert_eq!(ec1, deserialized);

    let serialized = serde_json::to_string(&ce2).unwrap();
    println!("serialized = {:?}", serialized);
    let deserialized: ConcreteEvent1 = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
    assert_eq!(ce2, deserialized);

    let serialized = serde_json::to_string(&ec2).unwrap();
    println!("serialized = {:?}", serialized);
    let deserialized: EventComparables = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
    assert_eq!(ec2, deserialized);

/****************************************************************************
 ****************************************************************************
 ****************************************************************************

           ########    ###    #### ##       #### ##    ##  ######
           ##         ## ##    ##  ##        ##  ###   ## ##    ##
           ##        ##   ##   ##  ##        ##  ####  ## ##
           ######   ##     ##  ##  ##        ##  ## ## ## ##   ####
           ##       #########  ##  ##        ##  ##  #### ##    ##
           ##       ##     ##  ##  ##        ##  ##   ### ##    ##
           ##       ##     ## #### ######## #### ##    ##  ######

 ****************************************************************************
 ****************************************************************************
 ****************************************************************************/

    if true {
        type PqType = PriorityQueue<ConcreteEvent1, EventComparables>;

        let mut pq: PqType = PriorityQueue::new();
        pq.push(ce1, ec1);
        pq.push(ce2, ec2);

        let serialized = serde_json::to_string(&pq).unwrap();
        println!("serialized = {:?}", serialized);
        let deserialized: PqType = serde_json::from_str(&serialized).unwrap();
        println!("deserialized = {:?}", deserialized);
    }
}

//////////////////////////////////////////////////////////////////////////////
// main()
//////////////////////////////////////////////////////////////////////////////

fn main() {
    test1();
    test2();
    test3();
}
@garro95 garro95 self-assigned this Oct 18, 2017
@garro95 garro95 added the bug label Oct 18, 2017
@garro95
Copy link
Owner

garro95 commented Oct 21, 2017

This test fails also using a standard HashMap in place of the PriorityQueue.
I think this is some sort of bug in serde

@garro95
Copy link
Owner

garro95 commented Oct 21, 2017

@ckaran can I use the modified version of your code to fill an issue directly to serde?

@ckaran
Copy link
Author

ckaran commented Oct 25, 2017

@garro95 My apologies for not replying sooner, I've been in and out sick for a bit. As for using my code, please, go ahead! Anything that can help fix things is good! Do you want me to create a pull request that converts the code into a unit test? Even if the problem is in serde, it affects your code as well, so have a test that catches the bug will be useful.

BTW, did you file a bug against serde for this? They really need to know about it. If you want to use my code as a basis for the bug, go ahead.

@garro95
Copy link
Owner

garro95 commented Oct 26, 2017

The bug was fixed and I used your code in unit tests. I told the serde guys of the bug in a IRC chat that we had.

@ckaran
Copy link
Author

ckaran commented Oct 31, 2017

@garro95 Thank you for fixing the bug!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants