3

I have the code (playground):

use std::collections::HashMap;

// We have some arbitrary struct (given values just placeholders)
struct SomeStruct {
    x: f32,
    y: usize,
}

fn main() {
    // We have some hashmap which contains the names (keys) and properties (values) of items.
    //  These are known at compile time.
    let hashmap: HashMap<&str, SomeStruct> = vec![
        ("a", SomeStruct { x: 2., y: 2 }),
        ("b", SomeStruct { x: 3.5, y: 1 }),
        ("c", SomeStruct { x: 0., y: 5 }),
    ]
    .into_iter()
    .collect();

    // We then have a bunch of data which arbitrarily references the names of these items.
    //  This data is also known at compile time.
}

Constantly typing "a","b", etc when needing to refer to the items is bad.

An enum could possibly be used to make this better, something like:

enum Items {
    A = SomeStruct { x: 2., y: 2 },
    B = SomeStruct { x: 3.5, y: 1 },
    C = SomeStruct { x: 0., y: 5 },
}

This would effectively be a const enum, such that when referring to these items in the future we could simply do Items::A or &Items::A rather than 'a' and having to do the necessary hashing.

It seems that doing this is not viable.

Is there a way to use a const enum? Or is there an alternative better solution?

While this question may be a duplicate of How can I create enums with constant values in Rust? the solutions proposed under that question did not work when using an arbitrary struct. The solution vallentin added does but this solution really applies more to this circumstance where the other solutions do not work. I think it provides a better clearer answer in context of this question where the other simpler solutions do not work.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58
  • 1
    The [accepted answer is "match in a function"](https://stackoverflow.com/a/36928678/155423), which is in this accepted answer, the [enum_map solution works for your case](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3258a9e55b99e7fbf4ad43815778cbe2), and the associated const answer — 3 of the 4 answers apply to your case, only the answers specific to primitives don't work. – Shepmaster Jul 06 '20 at 16:05

1 Answers1

8

You can use associated constants, which is similar to how the bitflags crate works. If you add #[non_exhaustive] to Items, you can prevent instantiation of Items.

#[non_exhaustive]
struct Items;

impl Items {
    pub const A: SomeStruct = SomeStruct { x: 2., y: 2 };
    pub const B: SomeStruct = SomeStruct { x: 3.5, y: 1 };
    pub const C: SomeStruct = SomeStruct { x: 0., y: 5 };
}

Alternatively, if you're using Nightly, you could have a match in a const method, using const_if_match. Which was also lately stabilized.

#![feature(const_if_match)]

enum Items {
    A,
    B,
    C,
}

impl Items {
    const fn value(self) -> SomeStruct {
        use Items::*;
        match self {
            A => SomeStruct { x: 2.0, y: 2 },
            B => SomeStruct { x: 3.5, y: 1 },
            C => SomeStruct { x: 0., y: 5 },
        }
    }
}
vallentin
  • 23,478
  • 6
  • 59
  • 81
  • 3
    I encourage you to move your answer to the duplicate question, as it would be a unique solution among the other answers. – Shepmaster Jul 06 '20 at 15:30
  • 1
    `#![feature(const_if_match)]` has been stabilized so the attribute isn't necessary on nightly anymore. It'll reach the next beta, and next-next stable. – mcarton Jul 06 '20 at 15:32