Skip to content
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 retain function #59

Merged
merged 1 commit into from
Aug 21, 2017
Merged

Add retain function #59

merged 1 commit into from
Aug 21, 2017

Conversation

Xaeroxe
Copy link
Contributor

@Xaeroxe Xaeroxe commented Aug 17, 2017

This change is Reviewable

Copy link
Member

@emilio emilio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have questions. Also, needs tests. Thanks!

lib.rs Outdated
}
}
Heap { ptr, capacity } => unsafe {
Vec::from_raw_parts(ptr, len, capacity).retain(f);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this drop the vector afterwards, causing a use after free the next time it's accessed? Also, I see nothing updating the ptr field, which may change I suppose.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be dropped afterwards. I think you should get the new pointer, len and capacity from the vec and mem::forget it afterwards.

lib.rs Outdated
if !f(&*array.ptr().offset(i as isize)) {
del += 1;
} else if del > 0 {
swap(&mut array.ptr().offset((i - del) as isize), &mut array.ptr().offset(i as isize));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the item has a destructor, what calls it?

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 17, 2017

@emilio I've addressed your comments and added tests, thanks for the help!

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 18, 2017

Travis only failed on nightly. Probably a regression in nightly.

EDIT: woops nevermind, looks like I accidentally broke nostd support. I just added a commit that should fix this.

Copy link
Member

@emilio emilio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me. Can you please squash the commits?

Maybe @mbrubeck or @jdm want to take another look at this, too.

Thanks!

lib.rs Outdated
}
}
if del > 0 {
self.len = len - del;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This can just be self.len -= del;, since del cannot be less than zero.

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 18, 2017

Squished and addressed your nit.

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 20, 2017

r? @mbrubeck and @jdm

lib.rs Outdated
}
}
}
if del > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need for the if, that's what I wanted to avoid in the first place :)

lib.rs Outdated
read(array.ptr().offset(i as isize));
del += 1;
} else if del > 0 {
swap((array.ptr().offset((i - del) as isize) as *mut A::Item).as_mut().unwrap(), (array.ptr().offset(i as isize) as *mut A::Item).as_mut().unwrap());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can't we just do self.swap(i - del, i)? We implement DerefMut<[A::Item]>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed if we do that and avoid the ptr::read, calling truncate() at the end, we can write this just with safe code, right?

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 20, 2017

@emilio Thanks for the review! The code has been greatly simplified and is no longer unsafe.

@jdm
Copy link
Member

jdm commented Aug 21, 2017

@bors-servo: r=emilio,jdm
This is much easier to reason about now; thanks!

@bors-servo
Copy link
Contributor

📌 Commit 5879b5e has been approved by emilio,jdm

@bors-servo
Copy link
Contributor

⌛ Testing commit 5879b5e with merge 6cbfbe2...

bors-servo pushed a commit that referenced this pull request Aug 21, 2017
Add retain function

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-smallvec/59)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

☀️ Test successful - status-travis
Approved by: emilio,jdm
Pushing 6cbfbe2 to master...

@bors-servo bors-servo merged commit 5879b5e into servo:master Aug 21, 2017
@Xaeroxe Xaeroxe deleted the retain branch August 21, 2017 05:14
@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 21, 2017

More out of curiosity than any kind of dire need, does this project have any kind of regular release cycle?

@jdm
Copy link
Member

jdm commented Aug 21, 2017

It is released whenever someone makes a PR that updated the Cargo.toml version.

@mbrubeck
Copy link
Collaborator

This didn't occur to me until just now, but we might want to change the retain signature to pass &mut A::Item to the callback instead of &A::Item. The Rust library team wishes they could make Vec::retain pass &mut T, and has used &mut for newer methods like HashMap::retain:

rust-lang/rust#39560 (comment)

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 21, 2017

Why would you want to mutate in a retain? It seems like you'd be better off doing

for thing in things {
     // Mutate thing
}
things.retain(/*closure*/);

I like this approach better as it's more clear that there is a side effect happening.

@mbrubeck
Copy link
Collaborator

for thing in things {
     // Mutate thing
}
things.retain(/*closure*/);

I like this approach better as it's more clear that there is a side effect happening.

Putting the mutation into a separate loop might require allocating additional memory to save intermediate state, while just using retain can use the results immediately. For example, if you want to send a message to a bunch of MPSC channels, and discard any senders whose receivers have hung up:

channels.retain(|sender| sender.send(message).is_ok());

This can't be done in two separate loops like the code above, without saving additional state.

Even in cases that can be easily separated, iterating over the collection twice instead can be a significant performance penalty. And since the retain function does have unique (&mut T) access to the collection, there's no reason for it to give out only shared (&T) references.

@Xaeroxe
Copy link
Contributor Author

Xaeroxe commented Aug 22, 2017

Alright that all makes sense. The current retain got released in 0.4.3 which still has 0 downloads so if we hurry and yank it we can still fix this.

@mbrubeck
Copy link
Collaborator

It might be less hassle to just fix this next time we make a release with breaking changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants