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)
    }
}
1 Like

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.

1 Like

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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.