Pass a refrence to an struct between different thread

Hi.
I am writing a whitelist struct that reads contents of a file and make a Hashset out of it and stores it inside struct.
Later in the program, i need to pass a reference of this struct to a closure to check the whitelist.
However, i don't know the right way to pass a reference of struct so that there is no performance penalty.
Maybe i am doing something wrong, i would be so happy if you help me correct it.
Thanks.

This is the simplified version of code:

pub struct WhiteList {
    set: HashSet<String>,
}
...
let whitelist = WhiteList::new();
loop {
    tokio::spawn(async move {
        &whitelist.is_allowed(&hostname)
    }
}

Are you spawning many tasks? Do you need to modify the value after creating it?

Yes, one task per every connection that is made to application so that is a lot.
And i do not need to edit it, only lookup on hashset by the method.

Create an Arc<WhiteList> and clone it for each spawn.

let whitelist = WhiteList::new();

// put stuff into the whitelist

// put it in an Arc (this makes it immutable)
let whitelist = Arc::new(whitelist);

loop {
    let whitelist = Arc::clone(&whitelist);
    tokio::spawn(async move {
        // .. use whitelist here
    });
}

This uses an Arc, which is a tool for sharing a value across many tasks or threads. Cloning an Arc is extremely cheap and does not clone the value inside it. Values are immutable once you put them in an Arc.

Wow, thanks, this was the exact thing i needed.

You're welcome. Note that it can sometimes make for clearer code to hide the Arc behind a custom struct.

pub struct WhiteListInner {
    set: HashSet<String>,
}

#[derive(Clone)]
pub struct WhiteList {
    inner: Arc<WhiteListInner>,
}

impl WhiteList {
    fn new(inner: WhiteListInner) -> Self {
        Self {
            inner: Arc::new(inner),
        }
    }
    fn contains(&self, value: &str) -> bool {
        self.inner.set.contains(value)
    }
}
let whitelist = WhiteListInner::new();

// put stuff into the whitelist

// put it in an Arc (this makes it immutable)
let whitelist = WhiteList::new(whitelist);

loop {
    let whitelist = whitelist.clone();
    tokio::spawn(async move {
        // .. use whitelist here
        if whitelist.contains("foo") {
            ...
        }
    });
}

But of course, you can do it either way.

1 Like

This is a very good idea, thanks again.