0

I'm trying to make my rust project more modular, where I have different integrations that should implement certain functions and have certain fields, along with additional ones specific to that integration. How can I make it so that a function parameter or struct field can accept any integration that implements the required functions?

Currently I'm using enums to do this, but the code is a bit sloppy.

enum SocialIntegration {
    Facebook(FacebookIntegration),
    Twitter(TwitterIntegration),
}

struct FacebookIntegration {
    pub username: String,
    pub url: String,
}

impl FacebookIntegration {
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        todo!()
    }
}

struct TwitterIntegration {
    pub username: String,
    pub url: String,
    following_list: Vec<String>,
}

impl TwitterIntegration {
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        todo!()
    }
}

struct IntegrationHelper {
    integrations: Vec<SocialIntegration>,
}

impl IntegrationHelper {
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        for integration in self.integrations {
            match integration {
                SocialIntegration::Facebook(integration) => integration.search(query),
                SocialIntegration::Twitter(integration) => integration.search(query),
            }
        }
    }
}

Coming from typescript, and previously used just an interface, and then creating a class implementing the interface. I couldn't find anything quite similar in Rust, or figure out a combination of things to use to achieve a similar result in Rust.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
zxt
  • 9
  • 1
  • 1
    Have you read about [traits](https://doc.rust-lang.org/book/ch10-02-traits.html)? – harmic Jun 23 '22 at 00:57
  • 1
    *"previously used just an interface... I couldn't find anything quite similar in Rust"* - If you haven't already, I encourage you to read through the Rust Book, in particular it has a section on [Traits: Defining Shared Behavior](https://doc.rust-lang.org/book/ch10-02-traits.html) – kmdreko Jun 23 '22 at 00:57

1 Answers1

1

Interfaces in other languages are analogous to traits in Rust. You can create a SocialIntegration trait:

trait SocialIntegration {
    fn search(&self, query: &str) -> Vec<SearchResult>;
}

And implement twice:

impl SocialIntegration for FacebookIntegration {
    fn search(&self, query: &str) -> Vec<SearchResult> {
        todo!()
    }
}

impl SocialIntegration for TwitterIntegration {
    fn search(&self, query: &str) -> Vec<SearchResult> {
        todo!()
    }
}

Inside IntegrationHelper you can then store boxed trait objects. This will let you store both facebook and twitter objects in the same vector and perform dynamic dispatch when calling their search methods.

struct IntegrationHelper {
    integrations: Vec<Box<dyn SocialIntegration>>,
}

impl IntegrationHelper {
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        let mut results = Vec::new();
        for integration in &self.integrations {
            results.append(&mut integration.search(query));
        }
        results
    }
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578