Declaring a global vector in rust

I have a requirement in my code which is - a constant array of string is used by more than one function. So I want to have something like below i.e a global variable vec at top of the file so that all the function can use the same the array.

//C++ code
static const vector<string> vec = {"str1","str2"};

How do I achieve this in rust?

Does this suit your needs?

const STRINGS: &[&str] = &["str1", "str2"];
4 Likes

Assuming both the vector and its elements are immutable, and the example from @cole-miller doesn't suit your needs, you can use Once for this.

1 Like

This C++ code is doing lots of implicit operations. The biggest roadblocker to implement this behavior as-is in Rust would be that it's static non-local variable without constant initialization support, which means it must be initialized even before the main() function is called. C++ runtime support this feature but the Rust doesn't.

https://en.cppreference.com/w/cpp/language/initialization

But you may not need all these implicit behaviors. To have an array of string literals, use @cole-miller 's code above instead.

6 Likes

I read from from this link that const are inlined during compile time so i want to use static instead . i tried below code but it failed

static COMPONENTS:Vec<&str> = vec![""];
fn main() {}
2 | static COMPONENTS:Vec<&str> = vec![""];
  |                               ^^^^^^^^ allocation not allowed in statics
  |
  = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)

any help here?

Take a look at lazy_static, I think that will help.

2 Likes

Do you really need Vec here? 'static slice can be put into static variable, as well as in the constant.

1 Like

Both consts and statics require a constant value, which a vector cannot be. Take @cole-miller 's example and just replace const with static:

static COMPONENTS: &[&str] = &[""];
4 Likes

Yes, it would inline the reference pointing to some global array of string literals. But why do you want to avoid it?

4 Likes

It's not necessarily global without static.

(There have also been some issues with more details that I failed to find.)

2 Likes

Here's the issue I was thinking of.

Better to use once_cell as it's going to be integrated into std.

2 Likes

Maybe this way works if Vec required.

static GLB_STRING_ARR: &[&str] = &["Hello", "World"];  // it's slice
static mut GLB_STRING_VEC: Vec<String> = Vec::new();   // it's Vec

fn main() {
    println!("{}", GLB_STRING_ARR.len());

    unsafe {
        GLB_STRING_VEC.push("Hello".to_string());
        GLB_STRING_VEC.push("World".to_string());
        println!("{}", GLB_STRING_VEC.len());
    }
}

Except that it's wrong: a static mut is unsafe to access for a reason – it's global and not automatically synchronized. The code as you wrote trivially has a race condition – please don't promote this kind of sloppy code unless you fully understand the implications of your recommendations.

@H2CO3 And how do you know the writer does not "fully understand the implications of [their] recommendations"? Your reply is off topic.

@H2CO3 is pointing out an incorrect solution, the incorrect solution is on-topic, so its correction necessarily is on-topic too. Moreoever using unsafe, static mut and global state are all patterns strongly discouraged here as we're trying to educate people. And since @xuxinhang is recommending those patterns without warnings or disclaimer, we could assume they don't actually know the tradeoffs they're making.

3 Likes

I know that by observing that the code that they recommended is in fact technically unsound, as it drops down to unsafe and mutates global state without manually ensuring the necessary synchronization of the value.

I know it's regarded as unsafe to access mutable static varibles through multiple threads, howerver i think it's okay to initialize such a varible in the main thread and do no more modifying from other threads (if used).

I just promote this solution after seeing the author seems prefering Vec to slice. If the author said there were more complicate accessing patterns, I wouldn't recommand such a solution.

1 Like

It's mine to post such a solution without further warnings for new-comers.

By the way, I also discourage using mutable global vars but not strongly - it's sometimes useful. I would be unhappy if had to repeat something like Rc<GlobalConfig> (async io here), which included by many structs, again and again.

1 Like

Sure, you can. But given that this is a forum where beginners seek help, people are going to jump in when someone suggests hazardous or unsound code is to point out that the code is hazardous or unsound, and also suggest to beginners that it is probably not anything they want to dive into without more experience with the language. Aside from guiding them to write idiomatic Rust, it would be irresponsible to do otherwise.

Sorry the topic got off track.

When you say "constant array", I take that to mean that the contents are all known at compile time and will not change, right? If so, you want what @kpreid and @cole-miller suggested:

static COMPONENTS: &[&str] = &["str1", "str2"];

It will mostly act like an immutable Vec. Most of the non-size-changing operations you're used to on a Vec are really operations on slices (like [&str]), because a Vec dereferences to a slice.

If you do need to build it once at runtime, you can use a OnceCell.

static COMPONENTS: Lazy<Vec<String>> = Lazy::new(|| {
    let mut v = Vec::new();
    let command = std::env::args_os()
        .map(|arg| arg.to_string_lossy().into_owned())
        .next()
        .unwrap_or_else(|| "This Program".to_string());

    v.push(command);
    v
});

Playground.

If you need to modify during runtime after you build it, you'll need some sort of synchronization as well, such as a Mutex.

static COMPONENTS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| {
    let mut v = Vec::new();
    let command = std::env::args_os()
        .map(|arg| arg.to_string_lossy().into_owned())
        .next()
        .unwrap_or_else(|| "This Program".to_string());

    v.push(command);
    Mutex::new(v)
});

Playground.

The other suggestions like lazy_static are variations on this approach.


Don't use static mut. You'll have to use unsafe to access it, and it's such a foot gun that experts get it wrong and it will likely be deprecated. It's the difficulty in avoiding undefined behavior that's the problem, not needing a global variable per se; in fact, OnceCell or something very much like it will likely become part of the standard library.

7 Likes