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 commented Aug 17, 2017

This change is Reviewable

@Xaeroxe Xaeroxe force-pushed the Xaeroxe:retain branch from a4ceea0 to 9f15d0a Aug 17, 2017
Copy link
Member

emilio left a comment

I have questions. Also, needs tests. Thanks!

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

This comment has been minimized.

@emilio

emilio Aug 17, 2017

Member

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.

This comment has been minimized.

@torkleyy

torkleyy Aug 17, 2017

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));

This comment has been minimized.

@emilio

emilio Aug 17, 2017

Member

If the item has a destructor, what calls it?

@Xaeroxe Xaeroxe force-pushed the Xaeroxe:retain branch from 9f15d0a to defa9e3 Aug 17, 2017
@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.

@Xaeroxe Xaeroxe force-pushed the Xaeroxe:retain branch from 8a7cdca to 54462fe Aug 18, 2017
Copy link
Member

emilio left a comment

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;

This comment has been minimized.

@emilio

emilio Aug 18, 2017

Member

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

@emilio
emilio approved these changes Aug 18, 2017
@Xaeroxe Xaeroxe force-pushed the Xaeroxe:retain branch 2 times, most recently from 4dcf0e4 to e250f0a Aug 18, 2017
@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

@emilio
emilio approved these changes Aug 20, 2017
lib.rs Outdated
}
}
}
if del > 0 {

This comment has been minimized.

@emilio

emilio Aug 20, 2017

Member

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());

This comment has been minimized.

@emilio

emilio Aug 20, 2017

Member

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

This comment has been minimized.

@emilio

emilio Aug 20, 2017

Member

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 Xaeroxe force-pushed the Xaeroxe:retain branch from e250f0a to 0b12ed4 Aug 20, 2017
@Xaeroxe Xaeroxe force-pushed the Xaeroxe:retain branch from 0b12ed4 to 5879b5e Aug 20, 2017
@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

bors-servo commented Aug 21, 2017

📌 Commit 5879b5e has been approved by emilio,jdm

@bors-servo
Copy link
Contributor

bors-servo commented Aug 21, 2017

Testing commit 5879b5e with merge 6cbfbe2...

bors-servo added 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

bors-servo commented Aug 21, 2017

☀️ 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
2 checks passed
2 checks passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
@Xaeroxe Xaeroxe deleted the Xaeroxe:retain branch Aug 21, 2017
@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
Contributor

mbrubeck commented Aug 21, 2017

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
Contributor

mbrubeck commented Aug 22, 2017

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
Contributor

mbrubeck commented Aug 22, 2017

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
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

6 participants
You can’t perform that action at this time.