Help implementing a function similar to this Python one


#1

I’ve been looking over the source code of the Python driver for RethinkDB at https://github.com/rethinkdb/rethinkdb/blob/3ec0642a567d31061072737fb57c85138870dba4/drivers/python/rethinkdb/ast.py#L51

There is a function which essentially receives any type as its argument and returns an instance of a subclass of the RqlQuery class.

Supposing I had a trait RqlQuery in Rust, how could I achieve a similar result to the below Python function (edited for brevity)?

def expr(val, nesting_depth=20):  # Returns an instance of a RqlQuery subclass
    '''
        Convert a Python primitive into a RQL primitive value
    '''
    if isinstance(val, RqlQuery):
        return val
    elif isinstance(val, collections.Callable):
        return Func(val)
    elif isinstance(val, (datetime.datetime, datetime.date)):
        return ISO8601(val.isoformat())
    elif isinstance(val, (str, unicode)):
        return Datum(val)
    elif isinstance(val, bytes):
        return Binary(val)
    elif isinstance(val, collections.Mapping):
        obj = {}
        for k, v in dict_items(val):
            obj[k] = expr(v, nesting_depth - 1)
        return MakeObj(obj)
    elif isinstance(val, collections.Iterable):
        val = [expr(v, nesting_depth - 1) for v in val]
        return MakeArray(*val)
    else:
        return Datum(val)

#2

isinstance(val, collections.Mapping) and isinstance(val, collections.Iterable) are problematic. There’s no way to ask “does x implement trait y” at runtime (you can only as “is x type y” using the Any trait).

This may be doable with specialization but that will only let you ask “does type x implement trait y” at compile time, not “does some unknown object x implement trait y” at runtime.

However, if you use concrete types instead of Mapping and Iterable, this is totally doable (although I recommend using an enum instead of traits.


#3

The typical way would be to have an enum instead of a trait, listing the various possibilities. There could then be a trait that converts into that enum, e.g.

enum Rql {
    Datum(String),
    Array(Vec<Rql>),
    // ...
}

trait ToRql {
    fn to_rql(self) -> Rql;
}

impl ToRql for String {
    fn to_rql(self) -> Rql { Rql::Datum(self) }
}
impl<T: ToRql> for Vec<T> {
    fn to_rql(self) -> Rql {
        Rql::Array(self.into_iter().map(|x| x.to_rql()).collect())
    }
}

// ...

#4

Thanks for pointing me on the right path. I’ll have a look at using an enum. Cheers.


#5

Thanks. Looks like an enum is the way to go.