How to allow the user to select which library of functions a struct will use internally?

I've made a small example in the Playground of my situation: Rust Playground

Defining a trait called Engine with methods set_msg and set_num doesn't help either, because one runs into various Size related issues (there area a bunch of other things that don't work in the following example; I've not bothered to get it working for now)?

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=1b4b816c8d405d822ea7ab1a03d3ce72

How do I go about implementing this pattern of having a Framework, and then at compile time (through a macro) have it use specific Engines from which it takes some functions whose names are standardized across Engines?

You might be able to use Feature Flags and conditional compilation for this.

Hi Michael,

Yes, that's probably what I'll have to do in the end, but I'm wondering if I can avoid that by making it more "programmatic"? That is, the user can set the library within their code, rather than within Cargo.toml?

--Brian

Why not just make the Engine implementations public and let the user construct a Framework with their particular engine (e.g. Framework::new_with_engine(EngineA::default()))? That's the easiest way for a user to choose which implementation they use.

If you don't want end users to be able to construct an engine, one way to encapsulate this is to have various constructors functions which create a Framework with the desired engine.

pub fn new_framework_a() -> Framework<EngineA> { ... }
pub fn new_framework_b() -> Framework<EngineB> { ... }

You can't really make your Framework generic over some engine type, E, and keep the concrete engine type private though, because you'll still be exposing it in the constructor function's return type.

It’s not that hard to get it to work

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d4f94c141a9963bd9679ab0d4ca33ba3

Hi steffahn: wow! Why is providing a marker: PhantomData<fn() -> E enough to make things work? (you can give me the name of the relevant concept and I can read up on it)

Thanks!

I suppose I initially know about this from

PhantomData - The Rustonomicon

The usage of PhantomData<fn() -> E> instead of PhantomData<E> is to communicate to the compiler that the Framework<E> does not actually or even logically actually contain a value of type E, otherwise you’ll get auto-traits bound on E, e.g. you’ll get an impl<E> Send for Framework<E> where E: Send with this unnecessary extra where bound E: Send if you don’t use e.g. fn() -> … to avoid it.

4 Likes

Thank you :smile:

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.