Skip to content

Commit 25d1c1c

Browse files
committed
FEAT: Implement a careful “MaybeUninit” using ManuallyDrop
This is a cautious version of MaybeUninit, since we don't have one in libstd, based on ManuallyDrop. This moves ArrayVec to a nice, no size overhead implementation by default. We use Rust version sniffing (build script) to automatically use this for new enough Rust versions. This doesn't kill nodrop unfortunately, it still remains as the fallback.
1 parent b197664 commit 25d1c1c

File tree

5 files changed

+102
-19
lines changed

5 files changed

+102
-19
lines changed

Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ repository = "https://github.com/bluss/arrayvec"
1111
keywords = ["stack", "vector", "array", "data-structure", "no_std"]
1212
categories = ["data-structures", "no-std"]
1313

14+
[build-dependencies]
15+
version_check = "0.1"
16+
1417
[dependencies]
1518
nodrop = { version = "0.1.12", path = "nodrop", default-features = false }
1619

@@ -37,12 +40,14 @@ harness = false
3740
[features]
3841
default = ["std"]
3942
std = []
40-
use_union = []
4143
serde-1 = ["serde"]
4244

4345
array-sizes-33-128 = []
4446
array-sizes-129-255 = []
4547

48+
# has no effect
49+
use_union = []
50+
4651
[package.metadata.docs.rs]
4752
features = ["serde-1"]
4853

build.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
//!
3+
//! This build script detects if we have new enough Rust
4+
//!
5+
6+
extern crate version_check;
7+
8+
fn main() {
9+
println!("cargo:rerun-if-changed=build.rs");
10+
if let Some((true, _)) = version_check::is_min_version("1.20.0") {
11+
println!("cargo:rustc-cfg=has_manuallydrop");
12+
}
13+
}

src/lib.rs

+16-18
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@
77
//! - Optional, enabled by default
88
//! - Use libstd; disable to use `no_std` instead.
99
//!
10-
//! - `use_union`
11-
//! - Optional
12-
//! - Requires Rust nightly channel
13-
//! - Experimental: This flag uses nightly so it *may break* unexpectedly
14-
//! at some point; since it doesn't change API this flag may also change
15-
//! to do nothing in the future.
16-
//! - Use the unstable feature untagged unions for the internal implementation,
17-
//! which may have reduced space overhead
1810
//! - `serde-1`
1911
//! - Optional
2012
//! - Enable serialization for ArrayVec and ArrayString using serde 1.0
@@ -28,6 +20,7 @@
2820
//!
2921
#![doc(html_root_url="https://docs.rs/arrayvec/0.4/")]
3022
#![cfg_attr(not(feature="std"), no_std)]
23+
3124
extern crate nodrop;
3225
#[cfg(feature="serde-1")]
3326
extern crate serde;
@@ -53,11 +46,16 @@ use std::fmt;
5346
#[cfg(feature="std")]
5447
use std::io;
5548

56-
#[cfg(not(feature="use_union"))]
57-
use nodrop::NoDrop;
5849

59-
#[cfg(feature="use_union")]
60-
use std::mem::ManuallyDrop as NoDrop;
50+
#[cfg(has_manuallydrop)]
51+
mod maybe_uninit;
52+
#[cfg(has_manuallydrop)]
53+
use maybe_uninit::MaybeUninit;
54+
55+
#[cfg(not(has_manuallydrop))]
56+
mod maybe_uninit_nodrop;
57+
#[cfg(not(has_manuallydrop))]
58+
use maybe_uninit_nodrop::MaybeUninit;
6159

6260
#[cfg(feature="serde-1")]
6361
use serde::{Serialize, Deserialize, Serializer, Deserializer};
@@ -96,7 +94,7 @@ unsafe fn new_array<A: Array>() -> A {
9694
///
9795
/// ArrayVec can be converted into a by value iterator.
9896
pub struct ArrayVec<A: Array> {
99-
xs: NoDrop<A>,
97+
xs: MaybeUninit<A>,
10098
len: A::Index,
10199
}
102100

@@ -133,7 +131,7 @@ impl<A: Array> ArrayVec<A> {
133131
/// ```
134132
pub fn new() -> ArrayVec<A> {
135133
unsafe {
136-
ArrayVec { xs: NoDrop::new(new_array()), len: Index::from(0) }
134+
ArrayVec { xs: MaybeUninit::uninitialized(), len: Index::from(0) }
137135
}
138136
}
139137

@@ -583,7 +581,7 @@ impl<A: Array> ArrayVec<A> {
583581
Err(self)
584582
} else {
585583
unsafe {
586-
let array = ptr::read(&*self.xs);
584+
let array = ptr::read(self.xs.ptr());
587585
mem::forget(self);
588586
Ok(array)
589587
}
@@ -612,7 +610,7 @@ impl<A: Array> Deref for ArrayVec<A> {
612610
#[inline]
613611
fn deref(&self) -> &[A::Item] {
614612
unsafe {
615-
slice::from_raw_parts(self.xs.as_ptr(), self.len())
613+
slice::from_raw_parts((*self.xs.ptr()).as_ptr(), self.len())
616614
}
617615
}
618616
}
@@ -622,7 +620,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
622620
fn deref_mut(&mut self) -> &mut [A::Item] {
623621
let len = self.len();
624622
unsafe {
625-
slice::from_raw_parts_mut(self.xs.as_mut_ptr(), len)
623+
slice::from_raw_parts_mut((*self.xs.ptr_mut()).as_mut_ptr(), len)
626624
}
627625
}
628626
}
@@ -638,7 +636,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
638636
/// ```
639637
impl<A: Array> From<A> for ArrayVec<A> {
640638
fn from(array: A) -> Self {
641-
ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) }
639+
ArrayVec { xs: MaybeUninit::from(array), len: Index::from(A::capacity()) }
642640
}
643641
}
644642

src/maybe_uninit.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
3+
use std::mem::ManuallyDrop;
4+
use std::mem::uninitialized;
5+
6+
/// A combination of ManuallyDrop and “maybe uninitialized”;
7+
/// this wraps a value that can be wholly or partially uninitialized;
8+
/// it also has no drop regardless of the type of T.
9+
pub struct MaybeUninit<T>(ManuallyDrop<T>);
10+
11+
impl<T> MaybeUninit<T> {
12+
/// Create a new MaybeUninit with uninitialized interior
13+
pub unsafe fn uninitialized() -> Self {
14+
Self::from(uninitialized())
15+
}
16+
17+
/// Create a new MaybeUninit from the value `v`.
18+
pub fn from(v: T) -> Self {
19+
MaybeUninit(ManuallyDrop::new(v))
20+
}
21+
22+
/// Return a raw pointer to the interior
23+
pub fn ptr(&self) -> *const T {
24+
(&self.0) as *const ManuallyDrop<_> as *const T
25+
}
26+
27+
/// Return a raw pointer to the interior (mutable)
28+
pub fn ptr_mut(&mut self) -> *mut T {
29+
(&mut self.0) as *mut ManuallyDrop<_> as *mut T
30+
}
31+
}
32+
33+
34+

src/maybe_uninit_nodrop.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
3+
use nodrop::NoDrop;
4+
use std::mem::uninitialized;
5+
6+
/// A combination of NoDrop and “maybe uninitialized”;
7+
/// this wraps a value that can be wholly or partially uninitialized.
8+
pub struct MaybeUninit<T>(NoDrop<T>);
9+
10+
impl<T> MaybeUninit<T> {
11+
/// Create a new MaybeUninit with uninitialized interior
12+
pub unsafe fn uninitialized() -> Self {
13+
Self::from(uninitialized())
14+
}
15+
16+
/// Create a new MaybeUninit from the value `v`.
17+
pub fn from(v: T) -> Self {
18+
MaybeUninit(NoDrop::new(v))
19+
}
20+
21+
/// Return a raw pointer to the interior
22+
pub fn ptr(&self) -> *const T {
23+
&**(&self.0)
24+
}
25+
26+
/// Return a raw pointer to the interior (mutable)
27+
pub fn ptr_mut(&mut self) -> *mut T {
28+
&mut **(&mut self.0)
29+
}
30+
}
31+
32+
33+

0 commit comments

Comments
 (0)