1

I am trying to implement a tree structure in Rust. My structure is like a JSON object, where properties can be simple values or subtrees of other JSON objects.

For leaves, I have an enum called ShyScalar:

#[derive(Clone, PartialEq, Debug)]
pub enum ShyScalar {
    Boolean(bool),
    Integer(i64),
    Rational(f64),
    String(String),
    Error(String)
}

For nodes, I am trying to create another enum, ShyValue. It can be a Scalar(ShyScalar) or an Object and I am trying to figure out how to implement the Object part. The core of it is the trait ShyAssociation, which lets me get or set properties using a string name, a crude form of reflection. Here is part of the implementation of the ShyAssociation trait, with a sample implementation using a HashMap.

pub trait ShyAssociation<'a> {
    /// Set the named property to a new value and return the previous value.
    /// If it is not permitted to set this property or it had no value previously, return None.
    fn set(&mut self, property_name: &'a str, property_value: ShyValue) -> Option<ShyValue>;

    /// Get the value of the named property.
    /// If the property has no value or the property does not exist, return None.
    fn get(&self, property_name: &'a str) -> Option<&ShyValue>;

    /// True if is is possible to set the named property. 
    /// This may be true even if the property does not currently have a value.
    fn can_set_property(&self, property_name: &'a str) -> bool;

    /// True if the property currently has a value that can be retrieved, false otherwise.
    fn can_get_property(&self, property_name: &'a str) -> bool;
}

impl<'a> ShyAssociation<'a> for HashMap<&'a str, ShyValue> {
    fn set(&mut self, property_name: &'a str, property_value: ShyValue) -> Option<ShyValue> {
        self.insert(property_name, property_value)
    }

    fn get(&self, property_name: &'a str) -> Option<&ShyValue> {
        HashMap::get(self, property_name)
    }

    fn can_set_property(&self, _property_name: &'a str) -> bool {
        true
    }

    fn can_get_property(&self, property_name: &'a str) -> bool {
        self.contains_key(property_name)
    }
}

This worked before the ShyValue included the Object variant.

I have tried using Box, and am investigating Rc and RefCell, but do not know how to compose things so that I can implement the three essential traits for Object: Clone, PartialEq and Debug.

I was able to get Clone working if I use the objekt crate, which implements ideas from How to clone a struct storing a boxed trait object?.

I cannot figure out how to compose things so as to implement all three traits.


The larger problem I am trying to solve is to build the context object (symbol table) for a rules engine. I need to be able to supply arbitrary objects that implement ShyAssociation to permit the rules engine to extract values using dot notation and set the results back into the objects. I am trying to get around the fact that Rust does not support reflection.


Additional information.

When I use objekt_clonable crate and try to compose with PartialEq and Debug, I get these errors:

the trait parser::shy_association::ShyAssociation cannot be made into an object

the trait parser::shy_association::ShyAssociation cannot be made into an object

note: the trait cannot use Self as a type parameter in the supertraits or where-clausesrustc(E0038) <::objekt::macros::__internal_clone_trait_object macros>(38, 55): the trait parser::shy_association::ShyAssociation cannot be made into an object

Here is some of the code I use:

use objekt_clonable::*;
#[clonable]
pub trait ShyAssociation<'a> :  Clone + PartialEq + Debug  {
    /// Set the named property to a new value and return the previous value.
    /// If it is not permitted to set this property or it had no value previously, return None.
    fn set(&mut self, property_name: &'a str, property_value: ShyValue) -> Option<ShyValue>;

    /// Get the value of the named property.
    /// If the property has no value or the property does not exist, return None.
    fn get(&self, property_name: &'a str) -> Option<&ShyValue>;

    /// True if is is possible to set the named property. 
    /// This may be true even if the property does not currently have a value.
    fn can_set_property(&self, property_name: &'a str) -> bool;

    /// True if the property currently has a value that can be retrieved, false otherwise.
    fn can_get_property(&self, property_name: &'a str) -> bool;
}
Paul Chernoch
  • 5,275
  • 3
  • 52
  • 73
  • It looks like your question might be answered by the answers of [How to test for equality between trait objects?](https://stackoverflow.com/q/25339603/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jul 31 '19 at 13:56
  • Also `pub trait ShyAssociation<'a>: Debug {...}` – Shepmaster Jul 31 '19 at 13:57
  • I will consult that solution and see if I can compose it with the Clone solution. My problem has not been individually implementing the Traits, it is getting all of them to work together without a compile error. – Paul Chernoch Jul 31 '19 at 15:41

0 Answers0