lesurp / OptionalStruct

Macro copying a struct with Option fields. Useful for config initialization
Apache License 2.0
36 stars 12 forks source link

Using the Applicable trait #29

Closed maxhbooth closed 1 month ago

maxhbooth commented 1 month ago

Hi, sorry for making another issue about this. I've been playing with the Applicable trait you made for https://github.com/lesurp/OptionalStruct/issues/22 (thanks, btw!), but I haven't quite been able to get it to work for me.

Unless I'm just overthinking this, it doesn't look like there is a way for me to use the Base of Applicable with other traits? E.g. I can't make an object that can be both the Base and call default on it, etc.

For context, our current usage with Applyable looks something like this this [1]:

pub trait Config: Default + Debug { ... }
pub trait OptionalConfig<Conf: Config>:  Applyable<Conf> + Default { ... }
pub fn init_config<Conf: Config, OptConf: OptionalConfig<Conf>>() -> Conf {
  let mut config = Conf::default();
  let opt_config = OptConf::default();
  opt_conf.apply_to(&mut config);
  debug!("our config: {config:?}");
  config
}

#[optional_struct]
#[derive(Config)]
pub struct Config1 { ... }

main() {
  let config = init_config::<Config1, OptionalConfig1>();
}

So, if were to use Applicable with the above example, I would have to pull anything using the Base object traits out. E.g. we would get something like this:

pub trait Config: Default + Debug { ... }
pub trait OptionalConfig:  Applicable + Default { ... }
pub fn new_init_config<OptConf: OptionalConfig>(mut config: OptConf::Base)  {
  let opt_config = OptConf::default();
  opt_conf.apply_to(&mut config);
}
pub fn use_config_traits(config: impl Config) {
    debug!("our config!: {config:?}");
}

#[optional_struct]
#[derive(Config)]
pub struct Config1 { ... }

main() {
  let mut config = Config1::Default();
  new_init_config::<OptionalConfig1>(config);
  use_config_traits(config);
}

And of course, if we had even more traits then it would only get more complicated. Anyway, would you have an example of a better way to do this anywhere? Or is this a limitation of how Applicable is defined?

[1] real source code found here: https://github.com/wayland-transpositor/wprs/blob/6b993332c55568e66961b52bb6285e76d97d50df/src/args.rs#L79

lesurp commented 1 month ago

Hey, no problem, glad to see people actually using this!

In your example you do not add a constraint Applicable<Base=Conf>, which is why you are unable to use it as a Base.

The code below should work as expected:

use optional_struct::*;
use std::fmt::Debug;

pub trait Config: Default + Debug {}

// ------------------------------------------------------------------------- This here
// ------------------------------------------------------------------------- V
pub trait OptionalConfig<Conf: Config>: Applicable<Base = Conf> + Default {}
pub fn init_config<Conf: Config, OptConf: OptionalConfig<Conf>>() -> Conf {
    let mut config = Conf::default();
    let opt_config = OptConf::default();
    opt_config.apply_to(&mut config);
    config
}

#[optional_struct]
#[derive(Debug, Default)]
pub struct Config1 {}

impl Config for Config1 {}
impl OptionalConfig<Config1> for OptionalConfig1 {}

fn main() {
    let config = init_config::<Config1, OptionalConfig1>();
}