[SOLVED] What pattern would you suggest for caching since there's no concept of global heap variables in rust?

#1

[Solution]
I ended up tying the state to the struct im implementing my app on.

I am making a cacher.
I built local caches for my apps like a billion times in golang before.
What I used to do was to create a global-local variable and have exported methods
which are internally locking and unlocking the data for a thread-safe store.
In rust, however, I cannot have a similar approach
since I cannot declare any global variables.
I tried to use const boxed map to no avail either.

What are you strategies for building this sort of libs in your app
without separating the entire logic as a service(which is the first thing came to my mind)
or having user to pass a state reference on every call?
What is the way for a module to be able to have an internal state?
[UPDATE]
here is a small example to demonstrate what im trying to do and what i get as a result

#2

You can have globals in rust so long as they are thread-safe. For instance, you can have a global Mutex or RwLock.

Because their constructors are not (yet) const fn, you need to use the lazy_static crate to initialize them.

extern crate lazy_static;

use lazy_static::lazy_static;
use std::sync::RwLock;
use std::collections::HashMap;

type Data = HashMap<String, String>;
lazy_static!{
    static ref CACHE: RwLock<Data> = RwLock::new(HashMap::new());
}
#3

wow this is really cool but there is too much magic and things that i don’t understand so i checked the source code and there’s whole bunch of cryptic macro stuff and what i understood is it just creates a struct with a field of type Lazy.
So after that i tried to create my own version using a const struct with a option field inside and set it to None and at runtime i try to update it to Some(whatever) but it just fails. So how is it possible that My code fails to store anything globally but lazy_static does it?

#4

A const is not the same thing as a static. A static is a thing that lives at a fixed memory address. A const is a compile-time expression and it is newly created every time you use it. (hence you cannot mutate it, you’re only mutating a temporary copy)

#5

So how do i actually make it work why does the lazy_static crate is allowed to do it but im not

#6

Without your code, that’s going to be pretty hard to determine. If you copy the lazy_static code into your own project you’ll see that it works and there’s nothing special about the lazy_static crate. You can also use a nightly compiler with -Z unstable-options --pretty=expanded to see exactly what the lazy_static macro expands to.

#7

@jethrogb here is an example code. This is exactly what im trying to do. But with the little rust provides me on this domain without magic its just not possible to get it working seems like

#8

You are completely missing the point.

The only thing lazy_static is doing is letting you call e.g. Mutex::new to initialize your static. If Mutex::new was a const fn (which it eventually will be) you wouldn’t even need the crate. All of the functionality of globals is built into rust; any static of type Mutex<T> or RwLock<T> will work just fine.

If you want to do what lazy_static does, you need to write unsafe code. That’s why it’s factored out into a highly-popular crate as a safe abstraction that everyone can use.

I’m going to type up an explanation of what lazy_static does anyways, but you probably won’t find it useful.

2 Likes
#9

This is the problem i dont understand you say by yourself Mutex::new is not a const fn and therefore i cannot call it, but lazy_static can

#10

lazy_static lets you initialize a static at runtime rather than compile time. That’s why it can call non-const functions.

2 Likes
#11

that’s exactly what I want. To declare a type then initialize it myself. And that’s the very thing compiler refuses me to do. So I have to use unsafe for that as I understand from your previous comment, correct?
that’s what I actually asking for there’s no safe way of doing this.

#12

lazy_static is a safe way, and common way, to do it.

#13

I think i found the answer. It seems static mut is exactly what i have been looking for

#14

This will require use of unsafe, so make sure you know what you’re doing so that you implement your cache correctly without data races. The primary bug you need to make sure you avoid is racey initialization of the global. lazy_static will let you do this without unsafe.

#15
  • static mut won’t work either. You still can’t initialize it!
  • static mut is virtually impossible to use correctly (i.e. every program that tries to use it sensibly technically invokes UB) and there have been talks of removing it from the language

That’s why we want static with interior mutability instead.

3 Likes
#16

@ExpHP yes static mut doesn’t work as well. this is the updated code and I cannot unwrap a static mut i don’t understand if I cannot unwrap it why it’s allowed in the first place. It exists but rust doesn’t let me see what’s inside of it.

So what you are saying is if I want to use rust and if I want to build a cache I have to rely on 3rd party crates because there’s no straight way to create anything global even using unsafe without going through unsafe pointers and memory locations.

#17

So it seems theres no way for me to build a cache in rust. I dont want to use third party crates for something this simple and esseantial and i cannot pass a state variable down with the function call because the function call will be a grpc call so at that context i will never have a local variable for state. and i dont want to build an entire service for this.

#18

every time i want to use rust for something in prod i hit a wall like this. Seriously this is an awful experience I have wasted literally 8+ hours for this today and there’s no way to do it in rust language in the end without magic

#19

Dude. Seriously. Can you wait 10 minutes for me to finish typing my post? :slight_smile:

1 Like
#20

oops sorry :slight_smile: I am so much frustrated i just cant help it :smiley: