Callback in multithreaded code

Hello!
I got this code

pub struct A<T> {
    list: Arc<RwLock<HashMap<u32, T>>>,
}

impl<T: Send + Sync> A<T> {
    fn new<C: Fn(T) + Send + Sync>(cleaner: C) -> Self {
        let list = Arc::new(RwLock::new(HashMap::new()));
        let res = Self {
            list: list.clone(),
        };

        spawn(move || {
            let c = cleaner;
            let l = list;
        });

        return res;
    }
}

and it's doesn't compile because of "the parameter type C may not live long enough" and "the parameter type T may not live long enough". You can try to compile in playground.
Why is it??? All this variables, clean and list move to closure! And T even not use directly, only contains in HashMap!

One more question - why rust wants T had Send + Sync boundaries? It's wrapped by RwLock which process any inter-threads access.

Generic types are allowed to be temporary references, e.g. the C callback could be borrowing a variable on stack, which would be unsafe to use from another thread.

To solve this, forbid these types from containing temporary references. This is done by adding 'static requirement:


impl<T: Send + Sync + 'static> A<T> {
    fn new<C: Fn(T) + Send + Sync + 'static>(cleaner: C) -> Self {
1 Like

I wrote this code right now. It looks like work. It was compile at least.
But I don't understand why should I use 'static lifetime. Not T neither C doesn't live so long!

Lifetimes apply only to borrows, not to owned objects (without inner references), e.g. i32 doesn't have a lifetime, &i32 has. Owned objects without any temporary references inside meet any lifetime requirement, because their lifetime is not limited to any scope.

I understand.
But, according the A struct definition

list: Arc<RwLock<HashMap<u32, T>>>,

it own a T! So why should I specify 'static lifetime to it?

T itself could also hold reference types. You need the 'static bound to indicate this doesn't happen

1 Like

You would own it only if it was an owned type, but T can be anything. It could be &str. Owning a type doesn't mean the type is thread-safe, for example you can own Cow<'a, str> wrapper, but it contains a temporary reference.

But if the A need a reference I should declare it like struct A<&T> isn't it?

No. T is a type, any type, and references are types, too.

You can say you want &T, and that can be a reference to an owned value, or a reference to a reference (e.g. &&str when T is &str).

1 Like

Thanks for all!

Another way to see T: 'static is that T can live an arbitrarily long time, because it's not borrowing anything else that would limit it. So that owned T is passed to the new thread, and the thread is allowed to keep that value as long as it wants. Whereas something like a T = &'a str can only live at most through the 'a lifetime.