jeikabu / runng

MIT License
25 stars 3 forks source link

Structs from runng do not implement `Debug` and might benefit from some other nice Traits. #25

Closed najamelan closed 5 years ago

najamelan commented 5 years ago

Right now if a struct contains for example a protocol::pair1::Pair1 it can't derive Debug because Pair1 doesn't implement it. It is generally accepted to more or less always derive or implement at least Debug for all pub structs.

Others that might be useful can be found in the rust book. Eq might also make sense for a socket. For the difference between Eq and PartialEq, see here.

Some traits are handy if it makes sense, like Copy, but they have implications for the future. If you later add a non-Copy property to your struct, you will have to break the API by removing Copy and instances will be implicitly copied on the stack which might not always be a good idea. However Clone is explicit and might be a good idea.

najamelan commented 5 years ago

I forgot to mention. Sometimes you have to explicitly set structs to be not Send or Sync if they are not threadsafe. Especially when working with C code it's good to think about this for every structure.

jeikabu commented 5 years ago

This one is trickier.

I've been using #derive[] sparingly on an "as-needed" basis. For example, NngError has Clone/Copy/Debug/PartialEq.

Most of the other structs are wrappers around C FFI bindings and not safe for Copy/Clone. Pretty much all of them have a function that needs to be called on Drop (nng_msg_free, nng_close, etc.). As much as possible I'd like to use Rust to enforce "correct" usage, but I haven't really settled on the best way to deal with this:

For example, once you've called create_async_context() I don't think it's safe to use the socket (because nng_aio will be using it), so the socket should probably get "consumed" (i.e. create_async_context(self) instead of create_async_context(&self)). But I need to ask the nng author to be sure. Pipes complicate this because they can be obtained from a message, but closing them will make the associated nng_ctx/nng_aio fail...

But I think you're right that there could be some improvements:

najamelan commented 5 years ago

Thanks for looking into this. This makes me wonder, in async code it is common to use closures and thus often one needs to "clone" things. In the little test app I posted, I thus end up cloning a protocol::pair1::Pair1 even though it's not in polyamorous mode. Is that a problem?

jeikabu commented 5 years ago

It's ok to clone it, it won't get closed until the last reference goes away. The Arc provides Send/Sync.

At the moment there's only impl Send for NngAio because I tend to write:

let a = factory.pair_open()?.listen(&url)?;
let b = factory.pair_open()?.dial(&url)?;

let a_thread = thread::spawn(move || -> NngReturn {
    let mut ctx = a.create_async_context()?;
    //...
});

I don't plan to use the synchronous functions like SendMsg::send() so I've not thought about that part of the API much. If you do plan to use that part, feel free to make suggestions or a PR. Come to think of it, I'm not even sure its got tests.