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 support for in-place map for Vecs of types with same size (take 3) #16853

Merged
merged 8 commits into from
Sep 15, 2014

Conversation

tbu-
Copy link
Contributor

@tbu- tbu- commented Aug 29, 2014

This is implemented using a new struct PartialVec which implements the proper
drop semantics in case the conversion is interrupted by an unwind.

For the old pull requests, see #15302, #16369.


let start = vec.as_mut_ptr();

// This `as int` cast is safe, because the size of the elements of the
Copy link
Member

Choose a reason for hiding this comment

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

This justification isn't actually required, since this is how offset is designed to work. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How is offset designed to work?

Actually I'm not even sure what I'm doing is correct, because the docs on ptr::offset say that the given offset must be inbound, which negative ones technically aren't.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, really? So the following is technically undefined?

let p = some_vec.as_ptr();
let q = p.offset(10);
let r = q.offset(-10);

However, it doesn't particularly matter since offset is widely used with the output of len(); this is not an unusual case (e.g. some_slice.iter() is created with offset and len).

Copy link
Contributor

Choose a reason for hiding this comment

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

@huonw: No, that q.offset(-10) is perfectly well defined if p.offset(10) was correct. It's within the bounds of the object it was based on.

@huonw
Copy link
Member

huonw commented Sep 5, 2014

cc @aturon

(Sorry that this sat unnoticed for so long @tbu-!)

@thestinger
Copy link
Contributor

I think this is a lot more complicated than it needs to be, even with failure safety in mind.

@tbu-
Copy link
Contributor Author

tbu- commented Sep 5, 2014

@thestinger What specifically? I had the impression that I did pretty much the minimum required to get this running.

///
/// Removes the next `T` from the vector and returns it as `Some(T)`, or
/// `None` if there are none left.
fn pop(&mut self) -> Option<T> {
Copy link
Member

Choose a reason for hiding this comment

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

Is this intentionally private?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this should be public.

@aturon
Copy link
Member

aturon commented Sep 8, 2014

@tbu- Can you elaborate on the use case you have in mind here?

I'm guessing the intent is just to support the map_inplace function. I'm wondering whether that could be the sole public interface, rather than exposing PartialVec? (My understanding is that the machinery here is needed to properly handle unwinding.) Or are there use cases you have in mind that go beyond map_inplace?

@aturon
Copy link
Member

aturon commented Sep 8, 2014

To elaborate slightly, it seems like the interface here effectively forces you to do something like your map_inplace, although technically you can pop off multiple elements before pushing in their replacements.

If PartialVec were private, I wonder whether the various methods it supplies could instead be moved directly into map_inplace? Basically, use PartialVec as a temporary place to move the Vec into, with the right drop semantics, but otherwise perform all of the work directly in map_inplace. That seems like it could simplify the code.

/// ]);
/// assert_eq!(u8s.as_slice(), &[[0x11, 0x22], [0x33, 0x44]]);
/// ```
pub fn map_inplace<U>(self, f: |T| -> U) -> Vec<U> {
Copy link
Member

Choose a reason for hiding this comment

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

Minor detail, but why not map_in_place?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know, it felt more natural to me, maybe because map_in_place sounds less like a hyphen between "in" and "place". But I have absolutely no problem in renaming this if you want it.

@tbu-
Copy link
Contributor Author

tbu- commented Sep 8, 2014

About the public interface: I don't know anymore why the PartialVec was exposed, but you're probably right that it can be hidden as an implementation detail.

However I am not sure putting less code in PartialVec and more code into map_inplace would simplify the code. In particular I think it's easier to see the closed PartialVec struct which provides a safe interface instead of doing the unsafe stuff in one function interleaved with the safe stuff.

@aturon
Copy link
Member

aturon commented Sep 8, 2014

@tbu- There is some amount of extra assertion checking, API documentation, and extra generality in the PartialVec methods that could go away if all the logic was made part of the map_inplace loop. Personally, I think it'd make this code simpler and more clear.

As far as interleaving safe and unsafe code, I think most of map_inplace would be unsafe, and I think that seeing both the loop and the memory operations it performs in one place will make it easier to validate the safety (because everything would fit on a single page, basically).

That said, I wouldn't block the PR on the basis of these preferences; my main concern is to hide PartialVec, which will give us the freedom to simplify things later.

I would vote for map_in_place though, mostly because the convention is to use an _ between each distinct word.

@tbu-
Copy link
Contributor Author

tbu- commented Sep 11, 2014

@aturon I renamed it to map_in_place and made everything else private.

When you finish reviewing it, please tell me so I can rebase it to master.

}
}

impl<T,U> Iterator<T> for PartialVec<T,U> {
Copy link
Member

Choose a reason for hiding this comment

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

Either the map_in_place method should use this iterator, or it should be dropped (as dead code).

@aturon
Copy link
Member

aturon commented Sep 12, 2014

@tbu- Looks good to me.

One minor point: now that the PartialVec interface is private, the asserts within push and into_vec could be made into debug_asserts, and the functions themselves could be marked unsafe, with map_in_place guaranteeing overall safety.

This would expand the use of unsafe a bit, but it would avoid unnecessary bounds checking on every element.

In any case, I'm happy to r+ as-is, after a rebase; we can always tweak things later. Thanks for doing this work!

@tbu-
Copy link
Contributor Author

tbu- commented Sep 12, 2014

@aturon I rebased it. Will continue to improve it based on your "optional" comments once this one lands.

@aturon
Copy link
Member

aturon commented Sep 12, 2014

@tbu- Okay, headed to bors. Thanks for your persistence on this one!

@tbu-
Copy link
Contributor Author

tbu- commented Sep 12, 2014

@aturon Thanks for reviewing it. Can you issue a retry? It's just a } that got lost during the rebase.

This is implemented using a new struct `PartialVec` which implements the proper
drop semantics in case the conversion is interrupted by an unwind.
This specifically includes:
- Fix of the tests
- Remove `transmute` between `Vec`s of different types
This is important because the underlying allocator of the `Vec` passes that
information to the deallocator which needs the guarantee that it is the same
parameters that were also passed to the allocation function.
This replaces the now obsolete syntax `&[]` with `[].as_slice()`.
@tbu-
Copy link
Contributor Author

tbu- commented Sep 14, 2014

I'm sorry for the failed build, I didn't update the tests when I rebased it to the master. Now I fixed the tests, ran make check successfully and rebased it again against current master.

bors added a commit that referenced this pull request Sep 15, 2014
This is implemented using a new struct PartialVec which implements the proper
drop semantics in case the conversion is interrupted by an unwind.

For the old pull requests, see #15302, #16369.
@bors bors closed this Sep 15, 2014
@bors bors merged commit 2c7f6ee into rust-lang:master Sep 15, 2014
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