Skip to content

WIP: Adopt to const generics #30

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

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
pull_request:
branches:
- master
-

name: Continuous integration

jobs:
Expand Down Expand Up @@ -96,7 +96,6 @@ jobs:
command: clippy
args: --all-targets --all-features -- -D warnings


- name: miri setup
uses: actions-rs/cargo@v1
if: matrix.rust == 'miri'
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ alloc = []
# - `exact_size_is_empty` (for implementing `<{Chunks,IterMove} as ExactSizeIterator>::is_empty` more effective)
nightly = []

array-impls-33-128 = []
array-impls-129-256 = []
[[example]]
name = "array_stack"
required-features = ["nightly"]

[package.metadata.docs.rs]
all-features = true
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ _Compiler support: requires rustc 1.41+_
## Examples

```rust
use arraylib::{Array, ArrayMap, ArrayExt};
use arraylib::Array;
// Array creation
let arr = <[_; 11]>::unfold(1, |it| {
let res = *it;
Expand All @@ -27,7 +27,7 @@ let arr = <[_; 11]>::unfold(1, |it| {
});

// Mapping
let arr = arr.map(|it| it * 2);
let arr = arr.lift(|it| it * 2);
assert_eq!(arr, [2, -4, 8, -16, 32, -64, 128, -256, 512, -1024, 2048]);

// By-value iterator
Expand Down
129 changes: 129 additions & 0 deletions examples/array_stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//! An example of using `Continuous` to build an array-based collection.
//!
//! All operations (except creation and unsizing) are defined on
//! `ArrayStack<[T]>` to make the codegen impact smaller (hoping for faster
//! compilation and smaller binaries).
//!
//! All nightly features are used only to make all operations `const`. Without
//! `const` restrictions this example works on stable.
//!
//! The example should also work without `std`/`alloc`.
#![feature(const_mut_refs)]
#![feature(const_maybe_uninit_as_ptr)]
#![feature(const_ptr_read)]
#![feature(const_raw_ptr_deref)]
#![feature(const_slice_from_raw_parts)]
#![feature(label_break_value)]
#![allow(clippy::redundant_pattern_matching)]

use core::mem::MaybeUninit;

use arraylib::Continuous;

#[repr(C)]
pub struct ArrayStack<S: ?Sized + Continuous> {
len: usize,
inner: S::Uninit,
}

impl<T, const N: usize> ArrayStack<[T; N]> {
const U: MaybeUninit<T> = MaybeUninit::uninit();

pub const fn new() -> Self {
Self {
len: 0,
inner: [Self::U; N],
}
}

pub const fn unsize(&mut self) -> &mut ArrayStack<[T]> {
use core::ptr::slice_from_raw_parts_mut;

// For some reason `ArrayStack<[T]>` doesn't implement `Pointee`, so it's
// impossible to use `core::ptr::from_raw_parts_mut` here.
//
// This function uses `slice_from_raw_parts_mut` to create a pointer with
// desired metadata.
let ptr = slice_from_raw_parts_mut(self as *mut Self as *mut T, N);

// ## Safety
//
// `ArrayStack<[T; N]>` and `ArrayStack<[T]>` are guaranteed to have the same
// layout. `ptr` contains a proper metadata (as long as slice metadata is the
// same as a dst with a slice metadata) and is derived from a mutable reference.
unsafe { &mut *(ptr as *mut ArrayStack<[T]>) }
}
}
impl<T> ArrayStack<[T]> {
pub const fn push(&mut self, item: T) -> Result<(), T> {
if self.len == self.inner.len() {
Err(item)
} else {
self.inner[self.len] = MaybeUninit::new(item);
self.len += 1;
Ok(())
}
}

pub const fn pop(&mut self) -> Option<T> {
match self.len {
0 => None,

// ## Safety
//
// `self.inner[..self.len]` is always initialized
_ => unsafe {
self.len -= 1;
Some(self.inner[self.len].as_ptr().read())
},
}
}

pub const fn peek(&self) -> Option<&T> {
match self.len {
0 => None,

// ## Safety
//
// `self.inner[..self.len]` is always initialized
_ => unsafe { Some(&*self.inner[self.len - 1].as_ptr()) },
}
}
}

const TEST: i32 = 'a: {
let mut stack = ArrayStack::<[i32; 2]>::new();

if let Err(_) = stack.unsize().push(1) {
break 'a -1;
}

let stack = stack.unsize();

let p0 = match stack.peek() {
None => break 'a -2,
Some(&x) => x,
};

if let Err(_) = stack.push(2) {
break 'a -3;
}

let p1 = match stack.peek() {
None => break 'a -4,
Some(&x) => x,
};

if let Ok(()) = stack.push(3) {
break 'a -5;
}

match (stack.pop(), stack.pop()) {
(Some(a), Some(b)) => p0 + p1 + a + b,
_ => -6,
}
};

fn main() {
assert_eq!(TEST, 6);
}
Loading