gtk-rs-core icon indicating copy to clipboard operation
gtk-rs-core copied to clipboard

properties: Figure out how to make use of the default value of enum properties automatically

Open sdroege opened this issue 2 years ago • 6 comments

See title

sdroege avatar Jan 30 '23 14:01 sdroege

To clarify the problem here...

Ideally we would do:

// Specialized impl for Default types
impl HasParamSpec for MyEnumType where Self: Default {
  type BuilderFn = fn(&str) -> ParamSpecEnumBuilder;
  fn param_spec_builder() -> Self::BuilderFn {
    // first argument, the name, is provided by the macro. Default value filled using the trait 
    |name| ParamSpecEnum::builder_with_default(name, Self::default())
  }
}

impl HasParamSpec for MyEnumType {
  type BuilderFn = fn(&str, Self) -> ParamSpecEnumBuilder;
  fn param_spec_builder() -> Self::BuilderFn {
    // first argument, the name, is provided by the macro. Default value provided by the user
    |name, default_value| ParamSpecEnum::builder_with_default(name, default_value)
  }
}

Sadly rust doesn't support specialization yet and I can't find an alternative way to achieve this.

ranfdev avatar Feb 04 '23 21:02 ranfdev

I have not looked at the new macro implementation, so I guess I am just being naive, but can't we just check if a default = ... is provided in the attributes? If yes, we generate builder_with_default, if not, we generate builder and then if the enum does not have a default it will fail to compile

pbor avatar Feb 05 '23 14:02 pbor

That requires adjusting the macro specifically to support Enums. That means other external types requiring a default value will not take advantage of this.

But yeah, that could work!

ranfdev avatar Feb 05 '23 15:02 ranfdev

Problem: the macro doesn't know at compile time if the property is an Enum, so I can't write

if type.is_enum() && there_is_default_attribute {
 call_builder_with_default(default_attribute_value)
}

So we are again without possible solutions.

ranfdev avatar Feb 06 '23 13:02 ranfdev

We could do something like #[property(..., default)] and if it finds that it uses a new HasParamSpecWithDefault trait, which can also be implemented for everything else that has a default value and uses Default::default()

sdroege avatar Feb 06 '23 13:02 sdroege

Inspired by that idea, I've tried to do

// unless a custom `default` attribute is specified, the macro will use this trait. 
pub trait HasParamSpecDefaulted: HasParamSpec + Default {
    type BuilderFnDefaulted = Self::BuilderFn;
    fn param_spec_builder_defaulted() -> Self::BuilderFnDefaulted {
        <Self as HasParamSpec>::param_spec_builder().default_value(Default::default())
    }
}

// Manually implement the trait for every Enum
impl<T: HasParamSpec<ParamSpec = ParamSpecEnum> + Default> HasParamSpecDefaulted for T {
    type BuilderFnDefaulted = fn(name: &str) -> ParamSpecEnumBuilder<T>;
    fn param_spec_builder_defaulted() -> Self::BuilderFnDefaulted {
        |name| Self::param_spec_builder(name, Default::default())
    }
}

In my mind, it should work. In Rust's mind error[E0658]: associated type defaults are unstable. It complains about type BuilderFnDefaulted = Self::BuilderFn;

ranfdev avatar Feb 06 '23 15:02 ranfdev