Best way to group trait objects with associated types


#1

Hi folks,

I wonder what is the best way in rust to group a bunch of trait objects with different associated types. To be specific:

trait Service {
    type Item;
    fn call(&self) -> Self::Item;
}

struct FooService;

impl Service for FooService {
    type Item = i32;

    fn call(&self) -> i32 {
        -123
    }
}

struct BarService;

impl Service for BarService {
    type Item = u32;

    fn call(&self) -> u32 {
        123
    }
}

fn main() {
    let services: Vec<Box<Service>> = Vec::new();
}

I want a way to put both FooService and BarService into the same collection, however, because it is required to specify the associated types at compile time, the above code won’t compile.

Thanks!


#2

How do you plan to use these trait objects if you are able to store them?


#3

Not knowing any more, is suggest creating an even type to hold them. That way you can even get the original objects back again later. To clarify, I’d create an entire time that will hold any one object, and then use the collection of your choice to hold those enums. As @vitalyd points out, without knowing the types of the objects, it’s hard to see another way you could use them.


#4

You made a good point. So I created an unified trait and changed my code to

#[macro_use]
extern crate serde_derive;
extern crate bincode;

use bincode::{deserialize, serialize};

trait Service {
    fn call(&mut self, req: &[u8]) -> Vec<u8>;
}

#[derive(Serialize, Deserialize, Debug)]
enum FooReq {
    Inc(i32),
    Get,
}

#[derive(Serialize, Deserialize, Debug)]
enum FooResp {
    Inc,
    Get(i32),
}

struct FooService {
    c: i32,
}

impl FooService {
    fn inc(&mut self, c: i32) {
        self.c += c;
    }

    fn get(&mut self) -> i32 {
        self.c
    }
}

impl Service for FooService {
    fn call(&mut self, req: &[u8]) -> Vec<u8> {
        let req : FooReq = deserialize(req).unwrap();

        match req {
            Inc(c) => serialize(self.add(c)).unwrap(),
            Get => serialize(self.get()).unwrap()
        }
    }
}

fn main() {}

The problem now is rust does not allow me to call self.add() or self.get() when implementing Service trait for FooService. In C++, You can do things like:

struct Service { vector<char> call(vector<char> &req) = 0; }

struct FooService : Service {
void inc(int c) {
...
}

int get() {
...
}

vector<char> call(vector<char> &req) override {
...
}
}

I wonder how to implement something similar in rust?


#5

By self.add() you mean self.inc()? I’m not entirely sure what issue you’re hitting - this is your code with inc() and get() called. I stubbed out serialize and deserialize because bincode isn’t available on the playground, but that shouldn’t change the picture.


#6

My bad, I should double check my code :(.