Skip to content

Commit 9c9dfca

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 16aabf7 commit 9c9dfca

File tree

6 files changed

+104
-19
lines changed

6 files changed

+104
-19
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ env:
55
matrix:
66
include:
77
- rust: 1.13.0
8+
- rust: 1.19.0
9+
- rust: 1.20.0
810
- rust: stable
911
env:
1012
- NODEFAULT=1

Cargo.toml

Lines changed: 6 additions & 1 deletion
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,9 +40,11 @@ harness = false
3740
[features]
3841
default = ["std"]
3942
std = []
40-
use_union = []
4143
serde-1 = ["serde"]
4244

45+
# has no effect
46+
use_union = []
47+
4348
[package.metadata.docs.rs]
4449
features = ["serde-1"]
4550

build.rs

Lines changed: 13 additions & 0 deletions
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

Lines changed: 16 additions & 18 deletions
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
@@ -25,6 +17,7 @@
2517
//!
2618
#![doc(html_root_url="https://docs.rs/arrayvec/0.4/")]
2719
#![cfg_attr(not(feature="std"), no_std)]
20+
2821
extern crate nodrop;
2922
#[cfg(feature="serde-1")]
3023
extern crate serde;
@@ -50,11 +43,16 @@ use std::fmt;
5043
#[cfg(feature="std")]
5144
use std::io;
5245

53-
#[cfg(not(feature="use_union"))]
54-
use nodrop::NoDrop;
5546

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

5957
#[cfg(feature="serde-1")]
6058
use serde::{Serialize, Deserialize, Serializer, Deserializer};
@@ -93,7 +91,7 @@ unsafe fn new_array<A: Array>() -> A {
9391
///
9492
/// ArrayVec can be converted into a by value iterator.
9593
pub struct ArrayVec<A: Array> {
96-
xs: NoDrop<A>,
94+
xs: MaybeUninit<A>,
9795
len: A::Index,
9896
}
9997

@@ -130,7 +128,7 @@ impl<A: Array> ArrayVec<A> {
130128
/// ```
131129
pub fn new() -> ArrayVec<A> {
132130
unsafe {
133-
ArrayVec { xs: NoDrop::new(new_array()), len: Index::from(0) }
131+
ArrayVec { xs: MaybeUninit::uninitialized(), len: Index::from(0) }
134132
}
135133
}
136134

@@ -574,7 +572,7 @@ impl<A: Array> ArrayVec<A> {
574572
Err(self)
575573
} else {
576574
unsafe {
577-
let array = ptr::read(&*self.xs);
575+
let array = ptr::read(self.xs.ptr());
578576
mem::forget(self);
579577
Ok(array)
580578
}
@@ -603,7 +601,7 @@ impl<A: Array> Deref for ArrayVec<A> {
603601
#[inline]
604602
fn deref(&self) -> &[A::Item] {
605603
unsafe {
606-
slice::from_raw_parts(self.xs.as_ptr(), self.len())
604+
slice::from_raw_parts((*self.xs.ptr()).as_ptr(), self.len())
607605
}
608606
}
609607
}
@@ -613,7 +611,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
613611
fn deref_mut(&mut self) -> &mut [A::Item] {
614612
let len = self.len();
615613
unsafe {
616-
slice::from_raw_parts_mut(self.xs.as_mut_ptr(), len)
614+
slice::from_raw_parts_mut((*self.xs.ptr_mut()).as_mut_ptr(), len)
617615
}
618616
}
619617
}
@@ -629,7 +627,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
629627
/// ```
630628
impl<A: Array> From<A> for ArrayVec<A> {
631629
fn from(array: A) -> Self {
632-
ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) }
630+
ArrayVec { xs: MaybeUninit::from(array), len: Index::from(A::capacity()) }
633631
}
634632
}
635633

src/maybe_uninit.rs

Lines changed: 34 additions & 0 deletions
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

Lines changed: 33 additions & 0 deletions
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)