gtk-rs-core
gtk-rs-core copied to clipboard
properties: Figure out how to make use of the default value of enum properties automatically
See title
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.
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
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!
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.
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()
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;