New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Sender::try_send_cow
#717
base: master
Are you sure you want to change the base?
Add Sender::try_send_cow
#717
Conversation
Can't we just use |
Definitely, but that's going to be a difficult function to name 😁 I'm happy to change it up if we can come up with a self-descriptive name. |
I am not really against using |
There is a very slight QoL benefit to using a Cow here, which I think should have virtually no overhead: fn send_with_extra_processing(ch: Sender<...>, data: Cow<'_, ...>, extra arguments...) -> ... {
// process the extra arguments or something that we dont want to duplicate the code of in our project :D
ch.try_send_cow(data)
}
fn send_in_loop() {
let data = Cow::Borrowed(&...);
for ch in ... {
send_with_extra_processing(ch, data.clone(), ...);
}
}
fn send_to_one() {
let data = Cow::Owned(...);
for ch in ... {
send_with_extra_processing(ch, data, ...);
}
} I say very slight because you can just do this instead, but this seems more unclear from a high-level API perspective, and delegates handling codepaths like this to the end user: fn send_with_extra_processing(ch: Sender<...>, data: Cow<'_, ...>, extra arguments...) -> ... {
// process the extra arguments or something that we dont want to duplicate the code of in our project :D
match data {
Cow::Borrowed(data) => ch.try_send_cloned(data),
Cow::Owned(data) => ch.try_send(data)
}
} |
As in, changing |
One thing to consider is that avoiding |
You meant to say this, right?: Cow::Borrowed(data) => ch.try_send_cloned(data), // there is no clone here
Personally, I'd like to keep the current I was referring to
crossbeam-channel doesn't target `no_std' iiuc because it inherently relies on dynamic allocation. |
Yes I did, thank you :D I would have no problem with What about There's also |
@@ -564,6 +534,32 @@ impl<T> Channel<T> { | |||
} | |||
} | |||
|
|||
impl<T: ToOwned<Owned = T>> Channel<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nice it looks more readable. However, I'd really just use Clone
instead of ToOwned<Owned = T>
because I think it's more concise and readable :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be, but Cow
's trait bound is ToOwned
; something that implements ToOwned
does not necessarily implement Clone
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every type that implements Clone
also implements ToOwned
: https://doc.rust-lang.org/stable/alloc/borrow/trait.ToOwned.html#impl-ToOwned-1. So Clone
basically always implies ToOwned
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - but ToOwned
does not implement Clone
and the trait bounds of Cow
use ToOwned
Thanks for the PR! I haven't reviewed the code yet, but I have a few questions.
|
Interesting idea - I will actually make a separate PR with this function since it sounds very useful. It would be awesome if crossbeam could actually provide both functions. The user can pick whatever they want to use :D
I'll have to draw up a test for this.
It would panic just like any normal panic - is there something to be concerned about here? Are there situations in crossbeam channels where panicking is bad? I suppose in a multi threaded context this could be a concern. I'll have to draw up a test for this too. |
} | ||
|
||
// Take ownership of/clone the underlying message. | ||
let msg = msg.into_owned(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Panicking here would be bad because it would stall the channel because write_unchecked
is never called. If we'd want to support panicking here, we'd have to include a Guard here which when a panic occurs "cancels" the to be written slot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too familiar on catching/handling panicking in Rust yet. Should we use these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better pattern for this case is RAII scope guard, such as scopeguard (because it has no additional cost).
IIRC there are not many types that can actually use Cow, so I feel providing only a method that takes a closure would probably be sufficient. |
This function's main purpose is to minimize memory allocations whilst using
Sender::try_send
in a loop and to make any code doing this cleaner, simpler and more idiomatic.The function takes a
Cow<'_, T>
instead ofT
as the message argument. This means you can providetry_send_cow
with either a borrowed reference to some data, or simply owned data for codepath convenience.For example, in this snippet,
bytes
will only be cloned and sent to the channel if it is not full and it is not disconnected. If all the channels are disconnected, this will not allocate any unnecessary memory.