-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Implement TryFrom<Box<[T]>> for Box<[T; N]> #47245
Conversation
This commit serves to provide the same benefits as PR rust-lang#44764 except for boxes that refer to owned arrays allocated in the heap.
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.
Is commit 781574787ae169ba4fe87a71730a5a2b1638761b a mistake? Why is there a merge commit in this PR?
} | ||
|
||
macro_rules! array_impls { | ||
($($N:expr)+) => { |
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.
You forgot to add $(...)+
around the item.
[00:05:12] Compiling alloc v0.0.0 (file:///checkout/src/liballoc)
[00:05:12] error: variable 'N' is still repeating at this depth
[00:05:12] --> liballoc/boxed.rs:665:47
[00:05:12] |
[00:05:12] 665 | impl<T> TryFrom<Box<[T]>> for Box<[T; $N]> {
[00:05:12] | ^^
[00:05:12]
[00:05:12] error: Could not compile `alloc`.
The functions are very short and there may be relatively unreasonable calling overhead if they're not inlined.
fn try_from(slice: Box<[T]>) -> Result<Box<[T; $N]>, TryFromSliceError<T>> { | ||
if slice.len() == $N { | ||
let ptr = Box::into_raw(slice) as *mut [T; $N]; | ||
unsafe { Ok(Box::from_raw(ptr)) } |
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.
@alexcrichton is this a valid operation?
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.
AFAIK yeah this should be safe, the destructor for Box<T>
should deallocate with the size of T
which in this case would be mem::size_of::<T>() * N
Could you give some examples where you'd use these conversions? |
Here a Redditor asked about how to go about making a heap-allocated table with a static size. I recommended using Having const generics as well as a safe conversion interface such as this one would solve his problem without the need for In my experience, LLVM seems to optimize further with arrays ( We currently have |
That post was asking about |
@sfackler to me it makes sense to have these conversions when we already have Currently, unsafe code is needed by libraries to make a statically sized box. If a statically-size slice is needed, multiple checks must be made for whether the box is the right size if authors choose to avoid |
?? let b: Box<[u64; 100]> = Box::new([0; 100]); |
I should have elaborated. Your example is possible with trivial/primitive types that implement struct Entry {
/* fields */
}
const NUM_ENTRIES: usize = 32;
lazy_static! {
static ref TABLE: Box<[Entry; NUM_ENTRIES]> = {
let mut table = Vec::<Entry>::with_capacity(NUM_ENTRIES);
for i in 0..NUM_ENTRIES {
table.push(Entry { /* ... */ });
}
table.into_boxed_slice().try_into().unwrap()
};
} By using the |
But how big is NUM_ENTRIES in practice? I'd expect it to almost always be larger than 32. |
Well, generally, perhaps. Unless this is something that's polled, one can't really give a proper number. Alas, it is likely to be greater than 32. |
Team member @dtolnay has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
@dtolnay proposal cancelled. |
/// array fails. | ||
#[unstable(feature = "try_from", issue = "33417")] | ||
#[derive(Clone)] | ||
pub struct TryFromSliceError<T>(Box<[T]>); |
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.
Please add a brief example involving TryFromSliceError. See FromUtf8Error for examples of examples.
/// be made. | ||
#[unstable(feature = "try_from", issue = "33417")] | ||
#[inline] | ||
pub fn into_boxed_slice(self) -> Box<[T]> { |
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.
Please add an example showing a case in which this method is useful. The point of the example wouldn't be to show how to call this method (we can assume readers know how to call methods when they get to browsing TryFrom documentation) but to show why someone would want to call this method.
type Error = TryFromSliceError<T>; | ||
|
||
#[inline] | ||
fn try_from(slice: Box<[T]>) -> Result<Box<[T; $N]>, TryFromSliceError<T>> { |
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.
Please add a test so that we don't break this implementation later. Something similar to the test in #44764 should work.
These changes serve to provide the same benefits as PR #44764 except for boxes, which refer to owned arrays allocated in the heap.
Should these changes be extended to other types like
Arc<[T]>
andRc<[T]>
?r? @sfackler