orium / rpds

Rust persistent data structures
Mozilla Public License 2.0
1.28k stars 58 forks source link

invalid memory reference for HashTrieMap #8

Closed candronikos closed 6 years ago

candronikos commented 6 years ago

Attempting to implement a wrapper for HashTrieMap and got the following error:

SIGSEGV: invalid memory reference

Most of the relevant code is below. It compiles but fails when running the test. Any idea what a fix would be for this?

type ContainedHval = Box<HVal>;

#[derive(Clone,Debug)]
pub struct HDict(HashTrieMap<String, ContainedHval>);

//pub type HDict<'b> = HashTrieMap<String, Box<HVal<'b>>>;

impl HVal for HDict {
    fn to_zinc(&self) -> String {
        "Not implemented!".to_string()
    }
}

impl Eq for HDict {}

impl PartialEq for HDict {
    fn eq(&self, other: &Self) -> bool {
        if self.0.size() != other.0.size() {
            return false;
        }

        for (k,v) in self.0.iter() {
            let tmp = match other.0.get(k) {
                Some(ov) => **v == **ov,
                None => false
            };

            if !tmp {
                return false;
            }
        }

        true
    }
}

impl HDict {
    pub fn new() -> HDict {
        HDict(HashTrieMap::new())
    }

    fn has(&self, name: &String) -> HBool {
        self.0.contains_key(name)
    }

    fn is_empty(&self) -> HBool {
        self.0.is_empty()
    }

    fn missing(&self, name: &String) -> HBool {
        !self.0.contains_key(name)
    }

    pub fn iter(&self) -> Iter<String, ContainedHval> {
        self.0.iter()
    }

    fn insert(&mut self, name: &String, value: ContainedHval) -> Self {
        HDict(self.0.insert(name.clone(),value))
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn create() {
        let a = HDict::new();
        let b = HDict::new().insert(&"Hello".to_owned(),Box::new(true));

        //assert_ne!(a,a.clone().insert(&"Hello".to_owned(),Box::new(true)));
        //assert_eq!(a,HDict::new());
        ///*
        assert_eq!(
            b.clone(),
            HDict::new().insert(&"Hello".to_owned(),Box::new(true))
        );
        //*/
    }
}
orium commented 6 years ago

Can you give me a complete program? That one is missing some definitions. (I changed some stuff to make it compile but the test ran without a problem.)

candronikos commented 6 years ago

Code at end of comment. I only want to implement PartialEq so I can perform unit tests for equality tests. Now that they're all in the one file, I'm getting a new error (maybe because of the double impl for the HVal trait and the HDict trait?)

What I find strange is that originally, the first two commented out asserts worked.

In my main code where these components are split into other modules:

New Error

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Aborted (core dumped)

Code

extern crate rpds;

use std::fmt::Debug;
use std::any::{Any,TypeId};

use rpds::HashTrieMap;
use rpds::map::hash_trie_map::Iter;

pub trait HVal: Any + Debug {
    fn to_zinc(&self) -> String;

    fn to_string(&self) -> String {
        self.to_zinc()
    }

    fn _get_type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

///*
impl Eq for HVal {}

impl PartialEq for HVal {
    fn eq(&self, other: &Self) -> bool {
        //self.tr_eq(other)
        match self._get_type_id() == other._get_type_id() {
            true => self == other,
            false => false
        }
    }
}
//*/

pub type HBool = bool;

impl HVal for HBool {
    fn to_zinc(&self) -> String {
        match *self {
            true => "T".to_string(),
            false => "F".to_string(),
        }
    }
}

type ContainedHval = Box<HVal>;

#[derive(Clone,Debug)]
pub struct HDict(HashTrieMap<String, ContainedHval>);

impl HVal for HDict {
    fn to_zinc(&self) -> String {
        "Not implemented!".to_string()
    }
}

///*
impl Eq for HDict {}

impl PartialEq for HDict {
    fn eq(&self, other: &Self) -> bool {
        if self.0.size() != other.0.size() {
            return false;
        }

        for (k,v) in self.0.iter() {
            let tmp = match other.0.get(k) {
                Some(ov) => **v == **ov,
                None => false
            };

            if !tmp {
                return false;
            }
        }

        true
    }
}
//*/

impl HDict {
    pub fn new() -> HDict {
        HDict(HashTrieMap::new())
    }

    fn has(&self, name: &String) -> HBool {
        self.0.contains_key(name)
    }

    fn is_empty(&self) -> HBool {
        self.0.is_empty()
    }

    fn missing(&self, name: &String) -> HBool {
        !self.0.contains_key(name)
    }

    pub fn iter(&self) -> Iter<String, ContainedHval> {
        self.0.iter()
    }

    fn insert(&mut self, name: &String, value: ContainedHval) -> Self {
        HDict(self.0.insert(name.clone(),value))
    }
}

fn main() {
    let a = HDict::new();
    let b = HDict::new().insert(&"Hello".to_owned(),Box::new(true));

    //assert_ne!(a,a.clone().insert(&"Hello".to_owned(),Box::new(true)));
    //assert_eq!(a,HDict::new());
    assert_eq!(
        b.clone(),
        HDict::new().insert(&"Hello".to_owned(),Box::new(true))
    );
}
orium commented 6 years ago

That's because HVal::eq() calls itself endlessly:

impl PartialEq for HVal {
    fn eq(&self, other: &Self) -> bool {
        match self._get_type_id() == other._get_type_id() {
            true => self == other,             // !!! This is a recursive call
            false => false
        }
    }
}