After I'm done with a
uv_close((uv_handle_t *)async, NULL)
Yes, you have to cast the
uv_handle_t*. That's how libuv internally works.
All handles share the base structure, so IIRC strict aliasing rules are not broken because it amounts to casting it to the first member of the structure.
A note on your example call to
uv_close: you can only free the memory for a handle in the close callback, not before, so if you pass NULL and the handle was allocated on the heap you won't know when you can free the memory.