Associated Consts vs alternatives for trait-defined data

I'm having a dilemma with a project of mine. I have the 'shape' of an object I want users to fill in the gaps for, but I don't want to allocate a new and complete structure each time part of it's asked for. My design goals are:

  • Strongly-typed
  • Static
  • Add as a generic param to a struct field

I've been using functions on traits to achieve this up to now, kinda like this:

trait TextMapping where Self: Mapping {
    fn analyzer() -> Option<&'static str> { None }
    fn boost() -> Option<f32> { None }
}

struct MyTextMapping;
impl TextMapping for MyTextMapping {
    fn analyzer() -> Option<&'static str> { Some("value") }
}

I'm not a huge fan of it though, because it's a verbose way to define a datastructure... but does ensure you only have one possible value for any implementation.

So I'm looking at alternatives; associated consts and a plain old struct returned by a method.

With an associated const:

trait TextMapping where Self: Mapping {
    const ANALYZER: Option<&'static str> = None;
    const BOOST: Option<f32> = None;
}

struct MyTextMapping;
impl TextMapping for MyTextMapping {
    const ANALYZER: Option<&'static str> = Some("value");
}

And with a plain struct:

#[derive(Default)]
struct TextMappingFields {
    pub analyzer: Option<&'static str>,
    pub boost: Option<f32>
}

trait TextMapping where Self: Mapping {
    fn mapping() -> &'static TextMappingFields;
}

const MYTEXTFIELDS: TextMappingFields = TextMappingFields {
    analyzer: Some("value"),
    ..Default::default()
};
struct MyTextMapping;
impl TextMapping for MyTextMapping {
    fn mapping() -> &'static TextMappingFields { MYTEXTFIELDS }
}

I'm guessing there's only one value for an associated const that's returned whenever you ask for it, which makes it better than the functions impl. But it only works on nightly... It's also not too far off what I'm already doing.

A struct seems a bit more idiomatic, supports stable and is probably easier to generate macros around, but is more user-code and with my design means I have another generic parameter I need to pass around...

Sorry if this is a bit of a brain-dump... I've spent a while pondering this, and would appreciate some feedback on possible approaches :slight_smile:

1 Like