-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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 Vec::flatten (as unstable, of course) #61680
Conversation
r? @shepmaster (rust_highfive has picked a reviewer for you, use r? to override) |
FWIW I suspect we don't technically need the const generics here with some creative design. However, I suspect @Centril will want to raise language team concerns here? |
@Mark-Simulacrum I tried doing it with I'm hoping that @Centril would be ok with this since it's unstable, and the unstable message explicitly says that it's waiting on const generics stabilization before being stabilized itself. |
This has been a common question on IRC for years -- with people wanting to flatten Vecs of `[u8; 4]` colours or `[f32; 3]` linalg vectors. I'm irrationally excited that const generics has a reached a state where this works now. (Named after `Iterator::flatten`, which is the same conceptual operation, just slower and doesn't currently work right with array `Item`s.)
Yep; 👍 -- As long as no one wants to rely on const generics in a stable fashion I'm OK with it.
Uhm... |
@Centril I just mean that I wasn't trying to make a category theory point 😛 |
// But for types with an actual size these multiplications | ||
// cannot overflow as the memory is allocated in self, so don't | ||
// codegen a reference to panic machinery. | ||
(self.len() * N, self.capacity() * N) |
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.
(self.len() * N, self.capacity() * N) | |
(self.len() * N, self.capacity() * N) |
I'm not going to get into category theory, but I'll disagree with this point. fn main() {
let a = vec![Some(42), None, Some(99)];
let b: Vec<_> = a.into_iter().flatten().collect();
println!("{:?}", b);
}
Notably, this function is much less generic (and thus more powerful), so I think it should not have the same name. |
Code itself seems reasonable. I'm not sure I agree with the frequency of use.
Pitting your anecdata against my anecdata, I don't recall this ever coming up on Stack Overflow. Some nearby things I found with a few minutes of searching:
I guess the tracking issue for this could serve as the popularity contest for stabilization though. |
It would make sense to also have a version of I am a bit confused why we can't just make the bounds be |
// codegen a reference to panic machinery. | ||
(self.len() * N, self.capacity() * N) | ||
}; | ||
mem::forget(self); |
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's kind of unfortunate that we need to do a forget
here, but I can't really think of what the alternative would be. Converting to a box first would shrink the capacity, which isn't really desired.
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.
We could have into_raw_parts(self) -> (*mut T, usize, usize)
, the reverse of Vec::from_raw_parts
. It may be a slight footgun remembering which usize
is the length and capacity though.
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.
Yeah, in hindsight it would have been much better for from_raw_parts
to take *mut [T]
and usize
, rather than *mut T
, and two usize
s. Unfortunately, we can't change this now.
Continuing the naming bikeshed, I think something along the lines of |
I think any one word name is going to obscure the fact that this is a pretty specialty operation and make it look like it's more generic. An utterly verbose name would be I like the
|
The is probably the more common case, I agree.
I've been thinking that might be the right way to go, actually. It was talked about a bunch in rust-lang/rfcs#1915, and I think it could still be useful even with const generics. If we have things like So I wonder if I should abandon this for now and wait for something like that.
But it is the same as this: fn main() {
let a = vec![vec![42], vec![13], vec![99]];
let b: Vec<_> = a.into_iter().flatten().collect();
println!("{:?}", b);
} [42, 13, 99] (I just can't use |
Couldn't |
@clarfon The ones I've seen have wanted named fields, so it's more like |
@scottmcm that's very fair, although I feel like having precisely named fields like that is something that's not 100% necessary. With point and vector types, it's not really a detriment to use methods to obtain the components, and that it's reasonable to ask people to either a) reconstruct a new vector by component or b) use a separate method which returns a mutable reference whenever they want to modify components individually. Usually, vectors should be modified using vector functions, rather than scalar functions on their components. But this is my opinion and others probably disagree. Adding |
I'm missing a nuance of your example. Going really generic, we could have the hypothetical setup: unsafe trait HasSameMemoryLayoutAs<T> {}
impl Vec<T> {
fn flatten_in_place<U>(self) -> Vec<U>
where
U: HasSameMemoryLayoutAs<T>,
{
// Handwaving goes here
}
} Then rub some const generics on it to get blanket implementations for nested arrays... allow third-parties to participate in the trait... and I ran out of brainpower trying to think through the implications... |
I think I figured out the disconnect:
Is that right? If so, I agree with both positions 🙂 |
Yep. I think this is my primary issue with this addition.
Kind of. I'd quibble about "same thing" because while the results from both functions will compare equal, they must do different things otherwise you wouldn't need to add this new function 😉 Namely, the proposed function is guaranteed to perform zero memory allocation; it's effectively a fancy |
In the same vein as this change, what about transformations from Would any of that give us better names? Can this just be an |
I'm going to close this for now; it seems like the right thing for me to do here is go and see if I can come up with a more holistic proposal (maybe like #49792 (comment)) and write a libs RFC for that. Edits in the future:
|
This has been a common question on IRC for years -- with people wanting to flatten Vecs of
[u8; 4]
colours or[f32; 3]
linalg vectors. I'm irrationally excited that const generics has a reached a state where this works now.(Named after
Iterator::flatten
, which is the same conceptual operation, just slower and doesn't currently work right with arrayItem
s.)