-
Notifications
You must be signed in to change notification settings - Fork 313
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 zip_clones
, zips an iterator with clones of a value
#989
base: master
Are you sure you want to change the base?
Conversation
Hi there, foremost: Thanks for this. Honestly, I don't believe that is sufficiently useful to justify an own implementation. Is the intention to avoid the If my assumptions are correct, I personally would advise against inclusion of this. Aside: Couldn't the |
I think it is useful in many use cases, take for example mine: Avoiding the clone is not relevant only in zip contexts, but its the smoother way to clone something Thanks for the comment regarding |
Hm... I hope I don't miss the forest for the trees, but I am not convinced. Maybe @jswrenn or @Philippe-Cholet groks your examples. Could you share a boiled down version of your use case to show why |
Here is a boiled down version of my use case: #[derive(Clone)]
struct Data {
values: Vec<f64>,
metadata_field1: usize,
metadata_field2: usize,
// ...
}
struct Component {
inputs: Vec<Receiver<Data>>,
outputs: Vec<Sender<Data>>,
}
impl Component {
fn component_main(&self) {
let mut sel = Select::new();
for input in self.inputs.iter() {
sel.recv(input);
}
loop {
let oper = sel.select();
let idx = oper.index();
let data = oper.recv(&self.inputs[idx]).unwrap();
let result = self.process_data(data);
for output in self.outputs.iter() {
output.send(result.clone()).unwrap();
}
}
}
fn process_data(&self, data: Data) -> Data {
unimplemented!()
}
} Each The above implementation use a trivial approach cloning the result for each output. for output in self.outputs.iter() {
output.send(result.clone()).unwrap();
} Given a for (output, result) in self.outputs.iter().zip_clones(result) {
output.send(result).unwrap();
} Without such a method, you could implement it as follows: if !self.outputs.is_empty() {
for output in self.outputs[..self.outputs.len() - 1].iter() {
output.send(result.clone()).unwrap();
}
self.outputs[self.outputs.len() - 1].send(result).unwrap();
} The above relay on the fact the outputs are stored in a vector, as we must know the number of outputs. let mut outputs = self.outputs.iter();
let mut cur = outputs.next();
if cur.is_some() {
let mut next = outputs.next();
while next.is_some() {
cur.unwrap().send(result.clone()).unwrap();
cur = next;
next = outputs.next();
}
cur.unwrap().send(result).unwrap();
} To emphasise, I want to avoid the last clone because many times a component is connected to only one other component, namely it has a single output, and no allocation should be done at all. Even for cases in which I have few outputs, the data packets are flowing in high frequencies and the last clone is redundant. |
@phimuemue @jswrenn @Philippe-Cholet what do you think? |
Hi there, my opinion has not changed. I retract from this PR to make way for @jswrenn or @Philippe-Cholet if they want to take over. |
I've run into the "want to avoid an unnecessary clone" problem with iterators in the past, and I'm happy to consider solutions to the problem. Things are a little chaotic at work right now, but I'll give this a thorough review as soon as I'm able. |
src/zip_clones.rs
Outdated
let mut iter = i.into_iter(); | ||
let next = iter.next(); |
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.
Iterator adapters are, ideally, lazy — i.e., they don't advance the underlying iterator when they're merely created.
Is there any way to avoid this peeking behavior altogether?
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.
In general, I dont think so, because you need to know if the current call to next()
should clone the zipped value or consume it, so you must peek.
We can maybe avoid the peek during the creation of the iterator, but not the peeks during next()
calls. tell me if you think it has any value.
Similar to `iter.zip(repeat_n(val, n))' but does not require knowing n
@jswrenn patched the PR |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #989 +/- ##
==========================================
+ Coverage 94.38% 94.48% +0.09%
==========================================
Files 48 50 +2
Lines 6665 6818 +153
==========================================
+ Hits 6291 6442 +151
- Misses 374 376 +2 ☔ View full report in Codecov by Sentry. |
Similar to `iter.zip(repeat_n(val, n))' but does not require knowing n