Need help in making function generic

I have a trait:

trait ToValue<T: Sized> {
    fn get_value(&self, symbols: &HashMap<String, Symbol>) -> Result<T, CustomError>;
    fn serialize(&self) -> Result<Value, CustomError>;
}

And some implementations for that trait:

impl ToValue<i32> for i32 {
    fn get_value(&self, _symbols: &HashMap<String, Symbol>) -> Result<i32, CustomError> {
        Ok(*self)
    }

    fn serialize(&self) -> Result<Value, CustomError> {
        Ok(json!(self))
    }
}

I want to make the function signature generic instead of hardcoding i32:

fn deserialize(
        values: &Vec<Value>,
    ) -> Result<Vec<(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>)>, CustomError> { ...}

Making the function generic would mean to add a generic type parameter

fn deserialize<T>(
    values: &Vec<Value>,
) -> Result<Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>, CustomError> {
    …
}

this may or may not be what you’re after.

I tried it but did not seem to work somehow. I will try once again.

If it doesn’t work, the problem is in (or related to) a part of the code you haven’t shared yet, be it the implementation of deserialize or a call-site.

I get an error with following function signature:

fn deserialize<T, U>(
        values: &Vec<Value>,
        x: String,
        y: String,
    ) -> Vec<Result<(Box<dyn ToValue<T>>, Box<dyn ToValue<U>>), CustomError>> { ... }
mismatched types
expected struct `Box<dyn ToValue<T>>`
   found struct `Box<dyn ToValue<i32>>`
mismatched types
expected struct `Box<dyn ToValue<U>>`
   found struct `Box<dyn ToValue<i32>>`

Where’s the error?

Try to give more complete code examples, otherwise it’s impossible to help.

Also more complete error messages help. Try to provide either the complete out put of cargo check/cargo build or at least everything related to the error in question.

1 Like

Just a sec, will post here. I am trying to trim down the noise in implementation since its pretty long.

// This function doesn't need to be generic, works fine, used by below function
fn deserialize_to_number(val: &Value) -> Result<Box<dyn ToValue<i32>>, CustomError> { ...}
fn deserialize<T, U>(
        values: &Vec<Value>,
        x: String,
        y: String,
    ) -> Result<Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<U>>)>, CustomError> {
        let init: Vec<Result<(Box<dyn ToValue<T>>, Box<dyn ToValue<U>>), CustomError>> = vec![];
        match x.as_str() {
            "A" => match y.as_str() {
                "B" => values
                    .iter()
                    .fold(init, |acc, val| {
                        acc.push(match val {
                            Value::Array(v) => match (v.first(), v.get(1)) {
                                (Some(v1), Some(v2)) => match (
                                    Self::deserialize_to_number(v1),
                                    Self::deserialize_to_number(v2),
                                ) {
                                // Error here on Ok((v3, v4))
                                    (Ok(v3), Ok(v4)) => Ok((v3, v4)),
                                    _ => Err(CustomError::Message(Message::ErrUnexpected)),
                                },
                                _ => Err(CustomError::Message(Message::ErrUnexpected)),
                            },
                            _ => Err(CustomError::Message(Message::ErrUnexpected)),
                        });
                        acc
                    })
                    .into_iter()
                    .collect(),
                _ => Err(CustomError::Message(Message::ErrUnexpected)),
            },
            _ => Err(CustomError::Message(Message::ErrUnexpected)),
        }
    }

Trimmed down the function a little more to understand whats bothering the compiler:

fn deserialize<T>(
        values: &Vec<Value>,
    ) -> Result<Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>, CustomError> {
        let init = vec![];
        values
            .iter()
            .fold(init, |mut acc, val| {
                acc.push(match val {
                    Value::Array(v) => match (v.first(), v.get(1)) {
                        (Some(v1), Some(v2)) => match (
                            Self::deserialize_to_number(v1),
                            Self::deserialize_to_number(v2),
                        ) {
                            (Ok(v3), Ok(v4)) => Ok((v3, v4)),
                            _ => Err(CustomError::Message(Message::ErrUnexpected)),
                        },
                        _ => Err(CustomError::Message(Message::ErrUnexpected)),
                    },
                    _ => Err(CustomError::Message(Message::ErrUnexpected)),
                });
                acc
            })
            .into_iter()
            .collect()
    }

Error changes to:

error[E0277]: a value of type `Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>` cannot be built from an iterator over elements of type `(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>)`
    --> src/main.rs:3057:14
     |
3057 |             .collect()
     |              ^^^^^^^ value of type `Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>` cannot be built from `std::iter::Iterator<Item=(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>)>`
     |
     = help: the trait `FromIterator<(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>)>` is not implemented for `Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>`
     = note: required because of the requirements on the impl of `FromIterator<Result<(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>), CustomError>>` for `Result<Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>, CustomError>`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
     |
2688 | impl Expression where Vec<(Box<dyn ToValue<T>>, Box<dyn ToValue<T>>)>: FromIterator<(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>)> {
     |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

To learn more, run the command again with --verbose.

Seems like the Box<dyn ToValue<i32>> from deserialize_to_number ends up in the Vec that deserialize is supposed to return.

I haven’t tried to really understand your code; maybe a generic function isn’t quite what you need here though? Or you’ll need to make deserialize_to_number generic, too? You could try to explain what deserialize does and how you (plan to) call it, including the information what you’re trying to achieve by making it generic.

Note that deserialize being generic would mean that the caller of deserialize gets to choose what T is (that’s why in the implementation of deserialize you cannot just turn a Box<dyn ToValue<i32>> into a Box<dyn ToValue<T>>; and that’s also why I’m asking about how you plan to call deserialize).

1 Like
  • I could do without a generic function for now. But this exponentially increases the non generic functions I will have to keep around. deserialize_to_number() does not need to be generic.

  • deserialize() takes a serde_json::Value as input, this value needs to a JsonArray consisting of other JsonArrays, each of inner JsonArray contains two serde_json::Value, both of these values need to be deserialized with non generic helper functions like deserialize_to_number().

  • result returned by deserialize() will be directly put(after unwrap) into an enum Variant like the one below.

enum MatchExpression {
    Expression(
        (
            Box<dyn ToValue<i32>>,
            // result returned by `deserialize()` will be filled be in as second argument to this variant
            Vec<(Box<dyn ToValue<i32>>, Box<dyn ToValue<i32>>)>,
            Box<dyn ToValue<i32>>,
        ),
    ),

I think I can use transmute() in this case since they should have same sizes? @steffahn

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.