How to impl Send to raw pointer?

let mut value = 1;
    let vp = &mut value as *mut i32;
    tokio::spawn(set_timeout(vp));
    loop {
        unsafe {
            if *vp != 1 {
                break;
            }
        }
    }
async fn set_timeout(vp: *mut i32) {
    sleep(Duration::from_millis(10000)).await;
    unsafe {
        *vp = 0;
    }
}

I have some code like this, but tokio complains that *mut i32 is not a Send so it failed.

You'll need to wrap it in a struct or other type and impl Send for that type:

struct Foo(*mut i32);
unsafe impl Send for Foo {}

This requires unsafe because you must do your own synchronization to prevent data races. You may want to use types like AtomicI32 or Mutex instead, if you don't have some other synchronization mechanism handy.

1 Like

The main question here is, why would you use raw pointer instead of, for example, AtomicUsize?

Because in my toy code, sometimes it doesn't need to check or lock, only one place would modify the value, and all other places are reading the value. I assume Atomic would also lock the value right?

Thank you, is there anyway not using a struct to wrap it?

You must wrap it in a struct to make it Send.

1 Like

If you're curious, the orphan rules are the reason why (Send is not a local trait and *mut i32 is not a local type).

1 Like

It might also help you to note that the pattern of wrapping a single field in a struct like that has the name newtype (the term can also be used to refer to the wrapper type itself), and it's free at runtime in the sense of imposing no memory overhead.

This is in stark contrast to e.g. Java where every type definition carries with it a nonzero runtime penalty.

1 Like

That's not a valid reason for not using synchronization primitives. This can still lead to all sorts of nasty bugs, like one thread seeing the updated value but not the other.

No, the stdlib guarantees that if an atomic type is available on the platform you're compiling to then it is lock-free. std::sync::atomic - Rust

3 Likes