Very new to Rust, what's the missing `base` error?

Coming from a C++ background, trying to learn Rust.

static mut states: HashMap<String, Box<dyn StateFunc>> = HashMap{};

Want to create a global hashmap and initialize it.

--> src/main.rs:10:58
     |
10 | static mut states: HashMap<String, Box<dyn StateFunc>> = HashMap{};
     |                                                        ^^^^^^^ missing `base`

already googled, no results. please help.

HashMap has private fields. You won't be able to make one by specifying an empty list of fields. You should use HashMap::new() to instantiate it instead.

1 Like

You're looking to call HashMap::new(), not instantiate a new HashMap using braces, because that struct has private fields, including base.

Thank you for your quick reply,

I forgot to mention, Hashmap::new() was my first version and got me an error. Hashmap{} seems to be suggested by the compiler or google, I forgot. Changed it back to Hashmap::new(), now getting

error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
--> src/main.rs:10:58
|
10 | static mut states: HashMap<String, Box<dyn StateFunc>> = HashMap::new();
|                                                          ^^^^^^^^^^^^^^

and also this version

--> src/main.rs:10:72
|
10 | static mut states: HashMap<String, Box<dyn StateFunc>> = HashMap<String, Box<dyn StateFunc>>::new();
|                                                                        ^ expected one of 7 possible tokens

Plus, the word "base" appears to be new to me. is it explained some where? Sorry for asking this, but a google search will give a lot of irrelevant results.

It's just the name of one of the (private) fields.

And yes, static items must be constructed using const functions (or other constructs usable in a const context) because they need to be evaluated at compilation time. So you won't be able to make a static mut HashMap currently, since its construction involves allocating memory upfront, and as such, its "constructor" function, HashMap::new(), is not and cannot be const.

By the way, static mut is rarely used in Rust, and it's an advanced topic, because its use requires unsafe. What are you trying to do? If you are trying to perform lazy initialization, use once_cell::Lazy instead.

I'm trying to implement a finite state machine with Rust. I know there are libraries, I intent to do this from scratch for learning.

In C++, one way is defining many states and inter-connecting them with pointers. But initializing them is very tiresome.

Instead, I want to pre-create a hashmap that saves all states. States can be referenced by a key.

All I need then, is defining a jump function for each state. I provide a condition to a state, it will output another state by looking into the global hashmap. The state itself won't hold a reference to the next states.

Another place I want to use a global is the output vector. The finite state machine will generate a series of tokens in a list. I wanted to create this list as a global.

because all states need to output to the list and I don't want to pass it around between the states.

Again, then it looks like you don't need static mut, you'll be fine with once_cell::Lazy, like this (playground):

use std::collections::HashMap;
use once_cell::sync::Lazy;

static STATES: Lazy<HashMap<String, u32>> = Lazy::new(|| {
    let mut hm = HashMap::new();

    hm.insert("foo".to_owned(), 1);
    hm.insert("bar".to_owned(), 2);
    hm.insert("qux".to_owned(), 3);

    hm
});

fn main() {
    println!("state `foo`: {}", STATES["foo"]);
}

Ok, this looks like a singleton class.

Thanks, I will check it out

If the state machine is fixed at compile time, you can use an enum and pattern matching to implement it:

#[derive(Copy,Clone)]
enum ParityMachine {
    Start, IsEven, IsOdd
}

impl ParityMachine {
    pub fn step(&mut self, input: u32) -> bool {
        use ParityMachine::*;
        let (newstate, result) = match (*self, input % 2) {
            (Start, 0) => (IsEven, false),
            (IsEven, 0) => (IsEven, true),
            (IsOdd, 0) => (IsOdd, false),
            (Start, 1) => (IsOdd, false),
            (IsEven, 1) => (IsOdd, false),
            (IsOdd, 1) => (IsEven, true),
            _ => unreachable!(),
        };
        *self = newstate;
        result
    }
}
2 Likes

Just a tip, If you want a globally static mutable anything, use OnceCell::lazy<Mutex<T>>. Lazy will allow you to lazily evaluate it when needed, mutex will provide a thread safe way to access it and provide interior mutability.

lazy_static! macro can also work in place of OnceCell, But I prefer not using the macro because it just looks cleaner to me.

1 Like

My previous code assumes your state machine is already in DFA form. If you want to execute an NFA directly, it’s not as straightforward (untested, but compiles):

use std::collections::HashSet;

type Input=char;

#[derive(Copy,Clone,Eq,PartialEq,Hash)]
enum State { Start }

impl State {
    fn step(self, inp:Option<Input>)->Vec<State> {
        unimplemented!()
    }

    fn is_accept(self)->bool {
        unimplemented!()
    }
}

pub struct NFA(HashSet<State>);
impl NFA {
    pub fn new()->NFA {
        let mut result = NFA(HashSet::new());
        result.0.insert(State::Start);
        result.close_epsilon();
        result
    }

    fn close_epsilon(&mut self) {
        loop {
            let mut result = self.0.clone();
            for &st in &self.0 {
                result.extend(st.step(None).into_iter());
            }
            if result == self.0 { return; }
            self.0 = result;
        }
    }

    pub fn step(&mut self, inp: Input)->bool {
        let mut result = HashSet::new();
        for &st in &self.0 {
            result.extend(st.step(Some(inp)).into_iter());
        }
        self.0 = result;
        self.close_epsilon();
        self.0.iter().any(|st| st.is_accept())
    }
}
1 Like
5 Likes

It makes me happy to know that error messages are still being improved :smiling_face_with_three_hearts:

1 Like

Btw if his answer solves your problem mark it as the solution, makes it easier for others reading this later. Think you could edit the title to better define the problem of "global static map" too.