0
mod math {
    #[derive(Debug)]
    pub struct SomeLevel {
        pub value: f64,
        pub priority: u8,
        pub color: String
    } 

    impl fmt::Display for SomeLevel {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "value: {}, priority: {}, color: {}", self.value, self.priority, self.color)
        }
    }

    pub static some_levels: &'static Vec<&'static SomeLevel> = &[
        SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
    ];
}

The error message I am getting is:

^ expected struct `Vec`, found array of 21 elements

I was doing vec![...] before, but I decided I want this as a static or constant collection.

Can someone help and explain what the issue is?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Blankman
  • 259,732
  • 324
  • 769
  • 1,199
  • 1
    Why do you need a `Vec` instead of just an array? – Andrew Sep 21 '22 at 03:50
  • 1
    I hope you don't need to fill out those `color: String` fields if you want them to be `static`. Only `String::new()` is a `const` function and can be used in a static initializer. You probably want to initialize your array lazily, check out the answers here: [How do I create a global, mutable singleton?](/q/27791532/2189130) – kmdreko Sep 21 '22 at 03:58
  • From the [vec documentation](https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees) "Vec will never perform a 'small optimization' where elements are actually stored on the stack..." so I don't think you can make a static vec. – Andrew Sep 21 '22 at 03:58
  • Does this answer your question? [How do I create a global, mutable singleton?](https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton) – Jmb Sep 21 '22 at 06:19

1 Answers1

3

Your first attempt:

    pub static SOME_LEVELS: &'static Vec<&'static SomeLevel> = &[
        SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
    ];

fails because a Vec is a completely different type to a static array in rust. A Vec is a dynamically growable heap allocated Vector type, while an array is a fixed size array, typically allocated on the stack.

You can correct this by altering some_levels to be an array (assuming that you don't actually need the facilities of a Vec):

        pub static SOME_LEVELS: &[SomeLevel; 1] = &[
            SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
        ];

This still does not compile though:

error[E0015]: cannot call non-const fn `<str as ToString>::to_string` in statics
  --> src/lib.rs:18:60
   |
18 |             SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
   |                                                            ^^^^^^^^^^^
   |
   = note: calls in statics are limited to constant functions, tuple structs and tuple variants

The reason for this error is that functions used to initialize a static need to be const, and to_string() is not const. It is possible to create an empty string using String::new(), which is a const function, but I am assuming that your real life lookup table would not contain only empty strings.

This leaves you with a couple of choices. If SomeLevel::color does not really need to be a String - for example if it is actually only ever assigned to fixed string literals - then you can change it to be &'static str:

        #[derive(Debug)]
        pub struct SomeLevel {
            pub value: f64,
            pub priority: u8,
            pub color: &'static str
        } 

        // ....

        pub static SOME_LEVELS: &[SomeLevel; 1] = &[
            SomeLevel { value: 0.0, priority: 1, color: ""},
        ];

Note that statics in rust are always immutable, so if this is the only use case of SomeLevel then most likely &'static str is what you want. If you really did need to have a String, then you would have to use lazy initialization of your static table, like this:

    use once_cell::sync::Lazy;

    // ...

    pub static SOME_LEVELS: Lazy<[SomeLevel; 1]> = Lazy::new(|| [
        SomeLevel { value: 0.0, priority: 1, color: "".to_string() },
    ]);

Here we use once_cell::sync::Lazy to lazily initialize SOME_LEVELS the first time it is referenced. It is initialized within a closure called by Lazy::new, which also means that the intialization value must be returned by value rather than reference.

Note: Lazy is also available in the standard library, under the name LazyCell, however it is not stabilized yet. The once_cell crate can be used in the meanwhile. lazy_static is another crate that provides similar functionality.

harmic
  • 28,606
  • 5
  • 67
  • 91