├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── codecov.yml ├── examples └── parsing.rs ├── src ├── impl.rs ├── lib.rs ├── macros.rs └── private.rs ├── tarpaulin.toml ├── tests ├── invalid-use │ ├── dependant_interior_mut.rs │ ├── dependant_interior_mut.stderr │ ├── get_dependant_with_static.rs │ ├── get_dependant_with_static.stderr │ ├── non_static_error.rs │ ├── non_static_error.stderr │ ├── steal_owned_data.rs │ ├── steal_owned_data.stderr │ ├── todo │ │ └── non_static_ref_into_f.rs │ ├── two_lifetimes_on_dependant.rs │ ├── two_lifetimes_on_dependant.stderr │ ├── use_dependant_ref_after_drop.rs │ └── use_dependant_ref_after_drop.stderr ├── test_invalid_use.rs └── test_zc.rs └── zc-derive ├── Cargo.toml └── src └── lib.rs /.github/ FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [avitex] 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | os: [ubuntu-latest] 8 | rust-toolchain: [stable, nightly] 9 | fail-fast: false 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | - name: Install Rust toolchain 15 | uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: ${{ matrix.rust-toolchain }} 18 | components: clippy, rustfmt 19 | override: true 20 | - name: Verify versions 21 | run: rustc --version && rustup --version && cargo --version 22 | - name: Cache build artifacts 23 | id: cache-cargo 24 | uses: actions/cache@v2 25 | with: 26 | path: | 27 | ~/.cargo/registry 28 | ~/.cargo/git 29 | target 30 | key: ${{ runner.os }}-cargo-${{ matrix.rust-toolchain }} 31 | - name: Test code with default features 32 | run: cargo test 33 | - name: Test code with all features 34 | run: cargo test --all-features 35 | - name: Lint code 36 | if: ${{ matrix.rust-toolchain == 'stable' }} 37 | run: cargo fmt -- --check && cargo clippy --all-features 38 | code_coverage: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Checkout code 42 | uses: actions/checkout@v2 43 | - name: Install Rust nightly components 44 | uses: actions-rs/toolchain@v1 45 | with: 46 | profile: minimal 47 | toolchain: nightly 48 | components: llvm-tools-preview 49 | - uses: Swatinem/fucov@v1 50 | - name: Upload to codecov.io 51 | uses: codecov/codecov-action@v1 52 | with: 53 | token: ${{secrets.CODECOV_TOKEN}} 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zc" 3 | version = "0.4.0" 4 | authors = ["avitex "] 5 | edition = "2018" 6 | description = "Self-referential zero-copy structure" 7 | categories = ["no-std", "memory-management", "data-structures"] 8 | documentation = "https://docs.rs/zc" 9 | homepage = "https://github.com/avitex/rust-zc" 10 | repository = "https://github.com/avitex/rust-zc" 11 | license = "MIT" 12 | readme = "README.md" 13 | include = ["src/**/*", "tests/**/*", "examples/**/*", "README.md", "LICENSE", "Cargo.toml"] 14 | keywords = ["parsing", "zero-copy"] 15 | 16 | [features] 17 | default = ["std", "derive"] 18 | std = ["alloc"] 19 | alloc = ["aliasable"] 20 | derive = ["zc-derive"] 21 | 22 | [dependencies] 23 | zc-derive = { version = "0.4", optional = true } 24 | aliasable = { version = "0.1.3", optional = true } 25 | 26 | [dev-dependencies] 27 | trybuild = "1.0" 28 | dangerous = "0.8" 29 | once_cell = "1.5" 30 | rustversion = "1" 31 | 32 | [workspace] 33 | members = [ 34 | ".", 35 | "./zc-derive", 36 | ] 37 | 38 | [patch.crates-io] 39 | zc-derive = { path = "./zc-derive" } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2021 James Dyson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/avitex/rust-zc/workflows/build/badge.svg)](https://github.com/avitex/rust-zc/actions?query=workflow:build) 2 | [![Coverage Status](https://codecov.io/gh/avitex/rust-zc/branch/master/graph/badge.svg?token=X2LXHI8VYL)](https://codecov.io/gh/avitex/rust-zc) 3 | [![Crate](https://img.shields.io/crates/v/zc.svg)](https://crates.io/crates/zc) 4 | [![Docs](https://docs.rs/zc/badge.svg)](https://docs.rs/zc) 5 | 6 | # rust-zc 7 | 8 | **Rust library providing `Zc` for self-referential zero-copy structures.** 9 | Documentation hosted on [docs.rs](https://docs.rs/zc). 10 | 11 | ```toml 12 | zc = "0.4" 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```rust 18 | use zc::Dependant; 19 | 20 | #[derive(PartialEq, Debug, Dependant)] 21 | pub struct StructWithBytes<'a>(&'a [u8]); 22 | 23 | impl<'a> From<&'a [u8]> for StructWithBytes<'a> { 24 | fn from(bytes: &'a [u8]) -> Self { 25 | Self(&bytes[1..]) 26 | } 27 | } 28 | 29 | fn main() { 30 | let owner = vec![1, 2, 3]; 31 | let data = zc::from!(owner, StructWithBytes, [u8]); 32 | 33 | assert_eq!( 34 | data.get::(), 35 | &StructWithBytes(&[2, 3]) 36 | ) 37 | } 38 | ``` 39 | 40 | ## Testing 41 | 42 | Run standard tests: 43 | 44 | ```sh 45 | cargo test 46 | ``` 47 | 48 | Run miri tests: 49 | 50 | ```sh 51 | cargo miri test --test test_zc 52 | ``` 53 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | coverage: 3 | status: 4 | patch: off 5 | github_checks: 6 | annotations: false 7 | -------------------------------------------------------------------------------- /examples/parsing.rs: -------------------------------------------------------------------------------- 1 | use dangerous::{BytesReader, Fatal, Input}; 2 | use zc::Zc; 3 | 4 | fn main() { 5 | let buf = Vec::from(&b"thisisatag,thisisanothertag"[..]); 6 | let parsed = Zc::new(buf, parse_bytes); 7 | dbg!(parsed.into_result().unwrap()); 8 | } 9 | 10 | fn parse_bytes(bytes: &[u8]) -> Result, ()> { 11 | dangerous::input(bytes) 12 | .read_all(parse) 13 | .map_err(|_: Fatal| ()) 14 | } 15 | 16 | fn parse<'i, E>(r: &mut BytesReader<'i, E>) -> Result, E> 17 | where 18 | E: dangerous::Error<'i>, 19 | { 20 | let mut parts = Vec::new(); 21 | loop { 22 | let s = r.take_until_opt(b',').to_dangerous_str::()?; 23 | parts.push(s); 24 | if !r.consume_opt(b',') { 25 | return Ok(parts); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/impl.rs: -------------------------------------------------------------------------------- 1 | use core::num::{ 2 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, 3 | NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping, 4 | }; 5 | 6 | use crate::Dependant; 7 | 8 | /////////////////////////////////////////////////////////////////////////////// 9 | // Dependant impl 10 | 11 | macro_rules! impl_dependant_ref { 12 | ($($ty:ty),*) => { 13 | $( 14 | unsafe impl<'o> Dependant<'o> for &'o $ty { 15 | type Static = &'static $ty; 16 | } 17 | )* 18 | }; 19 | } 20 | 21 | macro_rules! impl_dependant { 22 | ($($ty:ty),*) => { 23 | $( 24 | unsafe impl<'o> Dependant<'o> for $ty { 25 | type Static = $ty; 26 | } 27 | )* 28 | }; 29 | } 30 | 31 | impl_dependant_ref!(str, [u8]); 32 | 33 | impl_dependant!(()); 34 | impl_dependant!(bool, char); 35 | impl_dependant!(f32, f64); 36 | impl_dependant!(isize, usize); 37 | impl_dependant!(u8, u16, u32, u64, u128); 38 | impl_dependant!(i8, i16, i32, i64, i128); 39 | 40 | impl_dependant!( 41 | NonZeroI8, 42 | NonZeroI16, 43 | NonZeroI32, 44 | NonZeroI64, 45 | NonZeroI128, 46 | NonZeroIsize, 47 | NonZeroU8, 48 | NonZeroU16, 49 | NonZeroU32, 50 | NonZeroU64, 51 | NonZeroU128, 52 | NonZeroUsize 53 | ); 54 | 55 | unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for &'o T { 56 | type Static = &'static T::Static; 57 | } 58 | 59 | unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for Option { 60 | type Static = Option; 61 | } 62 | 63 | unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for Wrapping { 64 | type Static = Wrapping; 65 | } 66 | 67 | unsafe impl<'o, T, E> Dependant<'o> for Result 68 | where 69 | T: Dependant<'o>, 70 | E: Dependant<'o>, 71 | { 72 | type Static = Result; 73 | } 74 | 75 | /////////////////////////////////////////////////////////////////////////////// 76 | // alloc 77 | 78 | #[cfg(feature = "alloc")] 79 | mod alloc { 80 | use alloc::{ 81 | collections::{BTreeMap, BTreeSet, BinaryHeap}, 82 | string::String, 83 | vec::Vec, 84 | }; 85 | 86 | use aliasable::{boxed::AliasableBox, string::AliasableString, vec::AliasableVec}; 87 | 88 | use crate::{Dependant, Owner, Storage}; 89 | 90 | /////////////////////////////////////////////////////////////////////////// 91 | // Storage impl 92 | 93 | unsafe impl Storage for AliasableString {} 94 | unsafe impl Storage for AliasableVec {} 95 | unsafe impl Storage for AliasableBox {} 96 | 97 | /////////////////////////////////////////////////////////////////////////// 98 | // Owner impl 99 | 100 | impl Owner for String { 101 | type Storage = AliasableString; 102 | 103 | fn into_storage(self) -> Self::Storage { 104 | Self::Storage::from_unique(self) 105 | } 106 | 107 | fn from_storage(storage: Self::Storage) -> Self { 108 | Self::Storage::into_unique(storage) 109 | } 110 | } 111 | 112 | impl Owner for Vec { 113 | type Storage = AliasableVec; 114 | 115 | fn into_storage(self) -> Self::Storage { 116 | Self::Storage::from_unique(self) 117 | } 118 | 119 | fn from_storage(storage: Self::Storage) -> Self { 120 | Self::Storage::into_unique(storage) 121 | } 122 | } 123 | 124 | /////////////////////////////////////////////////////////////////////////// 125 | // Dependant impl 126 | 127 | impl_dependant!(String); 128 | 129 | unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for Vec { 130 | type Static = Vec; 131 | } 132 | 133 | unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for BTreeSet { 134 | type Static = BTreeSet; 135 | } 136 | 137 | unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for BinaryHeap { 138 | type Static = BinaryHeap; 139 | } 140 | 141 | unsafe impl<'o, K, V> Dependant<'o> for BTreeMap 142 | where 143 | K: Dependant<'o>, 144 | V: Dependant<'o>, 145 | { 146 | type Static = BTreeMap; 147 | } 148 | } 149 | 150 | /////////////////////////////////////////////////////////////////////////////// 151 | // std 152 | 153 | #[cfg(feature = "std")] 154 | mod std { 155 | use std::collections::{HashMap, HashSet}; 156 | use std::hash::BuildHasher; 157 | 158 | use crate::Dependant; 159 | 160 | /////////////////////////////////////////////////////////////////////////// 161 | // Dependant impl 162 | 163 | unsafe impl<'o, T, S> Dependant<'o> for HashSet 164 | where 165 | T: Dependant<'o>, 166 | S: BuildHasher + 'static, 167 | { 168 | type Static = HashSet; 169 | } 170 | 171 | unsafe impl<'o, K, V, S> Dependant<'o> for HashMap 172 | where 173 | K: Dependant<'o>, 174 | V: Dependant<'o>, 175 | S: BuildHasher + 'static, 176 | { 177 | type Static = HashMap; 178 | } 179 | } 180 | 181 | /////////////////////////////////////////////////////////////////////////////// 182 | // Dependant impl for tuples and arrays 183 | 184 | macro_rules! impl_dependant_tuple { 185 | ($($name:ident)+) => { 186 | unsafe impl<'o, $($name: Dependant<'o>),+ > Dependant<'o> for ($($name,)+) { 187 | type Static = ($($name::Static,)+); 188 | } 189 | } 190 | } 191 | 192 | // FIXME: Replace with const-generics 193 | macro_rules! impl_dependant_array { 194 | ($($n:literal)+) => { 195 | $(unsafe impl<'o, T: Dependant<'o>> Dependant<'o> for [T; $n] { 196 | type Static = [T::Static; $n]; 197 | })* 198 | } 199 | } 200 | 201 | impl_dependant_tuple!(T1); 202 | impl_dependant_tuple!(T1 T2); 203 | impl_dependant_tuple!(T1 T2 T3); 204 | impl_dependant_tuple!(T1 T2 T3 T4); 205 | impl_dependant_tuple!(T1 T2 T3 T4 T5); 206 | impl_dependant_tuple!(T1 T2 T3 T4 T5 T6); 207 | impl_dependant_tuple!(T1 T2 T3 T4 T5 T6 T7); 208 | impl_dependant_tuple!(T1 T2 T3 T4 T5 T6 T7 T8); 209 | 210 | impl_dependant_array!(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32); 211 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Crate providing [`Zc`] for self-referential zero-copy structures. 2 | 3 | #![no_std] 4 | #![forbid( 5 | clippy::pedantic, 6 | rust_2018_idioms, 7 | anonymous_parameters, 8 | unused_qualifications, 9 | missing_docs, 10 | trivial_casts, 11 | trivial_numeric_casts, 12 | unstable_features, 13 | unused_extern_crates, 14 | unused_import_braces, 15 | unused_results, 16 | warnings 17 | )] 18 | 19 | #[cfg(feature = "std")] 20 | extern crate std; 21 | 22 | #[cfg(feature = "alloc")] 23 | extern crate alloc; 24 | 25 | mod r#impl; 26 | // FIXME: Remove the need for macros. 27 | mod macros; 28 | mod private; 29 | 30 | use core::fmt::{self, Debug, Display}; 31 | use core::ops::Deref; 32 | 33 | #[cfg(feature = "alloc")] 34 | pub use aliasable; 35 | 36 | #[cfg(feature = "derive")] 37 | pub use zc_derive::Dependant; 38 | 39 | use self::private::{Construct, TryConstruct}; 40 | 41 | /// Zero-copy structure consisting of an [`Owner`] and a [`Dependant`]. 42 | pub struct Zc { 43 | // SAFETY: Order of fields is important for preventing dropping the storage 44 | // before the value that references it. 45 | value: D, 46 | storage: O::Storage, 47 | } 48 | 49 | impl Zc 50 | where 51 | O: Owner, 52 | D: Dependant<'static>, 53 | { 54 | /// Construct a new zero-copied structure given an [`Owner`] and a 55 | /// function for constructing the [`Dependant`]. 56 | /// 57 | /// # Example 58 | /// ``` 59 | /// use zc::{Zc, Dependant}; 60 | /// 61 | /// #[derive(Dependant)] 62 | /// struct MyStruct<'a>(&'a [u8]); 63 | /// 64 | /// impl<'a> From<&'a [u8]> for MyStruct<'a> { 65 | /// fn from(bytes: &'a [u8]) -> Self { 66 | /// Self(&bytes[1..]) 67 | /// } 68 | /// } 69 | /// 70 | /// let owner = vec![1, 2, 3]; 71 | /// let _ = zc::from!(owner, MyStruct, [u8]); 72 | /// ``` 73 | pub fn new(owner: O, constructor: C) -> Self 74 | where 75 | C: for<'o> Construct<'o, ::Target, Dependant = D>, 76 | { 77 | let storage = Owner::into_storage(owner); 78 | // Create a temporary dependant given the target reference. 79 | let value = unsafe { constructor.construct(storage.deref()) }; 80 | // Construct the zero-copy structure given the raw parts. 81 | Self { storage, value } 82 | } 83 | 84 | /// Try construct a new zero-copied structure given an [`Owner`] and a 85 | /// function for constructing the [`Dependant`]. 86 | /// 87 | /// # Example 88 | /// ``` 89 | /// use zc::{Zc, Dependant}; 90 | /// use core::convert::TryFrom; 91 | /// 92 | /// #[derive(Dependant)] 93 | /// struct MyStruct<'a>(&'a [u8]); 94 | /// 95 | /// impl<'a> TryFrom<&'a [u8]> for MyStruct<'a> { 96 | /// type Error = (); 97 | /// 98 | /// fn try_from(bytes: &'a [u8]) -> Result { 99 | /// Ok(Self(&bytes[1..])) 100 | /// } 101 | /// } 102 | /// 103 | /// let owner = vec![1, 2, 3]; 104 | /// let _ = zc::try_from!(owner, MyStruct, [u8]); 105 | /// ``` 106 | /// 107 | /// # Errors 108 | /// Returns `E` if the constructor failed. 109 | pub fn try_new(owner: O, constructor: C) -> Result 110 | where 111 | E: 'static, 112 | C: for<'o> TryConstruct<'o, ::Target, Error = E, Dependant = D>, 113 | { 114 | let storage = Owner::into_storage(owner); 115 | // Try create a temporary dependant given the target reference. 116 | match unsafe { constructor.try_construct(storage.deref()) } { 117 | Ok(value) => Ok(Self { storage, value }), 118 | Err(err) => Err((err, Owner::from_storage(storage))), 119 | } 120 | } 121 | 122 | /// Return a reference to the [`Dependant`]. 123 | /// 124 | /// The dependant type `T` must be supplied (eg. 125 | /// `Self::dependant::(&self)`). 126 | /// 127 | /// # Example 128 | /// ``` 129 | /// use zc::{Zc, Dependant}; 130 | /// 131 | /// #[derive(Debug, PartialEq, Dependant)] 132 | /// struct MyStruct<'a>(&'a [u8]); 133 | /// 134 | /// impl<'a> From<&'a [u8]> for MyStruct<'a> { 135 | /// fn from(bytes: &'a [u8]) -> Self { 136 | /// Self(&bytes[1..]) 137 | /// } 138 | /// } 139 | /// 140 | /// let owner = vec![1, 2, 3]; 141 | /// let data = zc::from!(owner, MyStruct, [u8]); 142 | /// 143 | /// assert_eq!( 144 | /// data.get::(), 145 | /// &MyStruct(&[2, 3]) 146 | /// ); 147 | /// ``` 148 | // FIXME: This interface isn't the nicest as you have to specific the 149 | // dependant again to retrieve it. GATs should provide us a way to make this 150 | // nicer with a generic associated lifetime. 151 | // See: https://github.com/rust-lang/rust/issues/44265 152 | pub fn get<'a, T>(&'a self) -> &T 153 | where 154 | T: Dependant<'a, Static = D>, 155 | { 156 | let value_ptr: *const D = &self.value; 157 | unsafe { &*value_ptr.cast::() } 158 | } 159 | } 160 | 161 | impl Zc 162 | where 163 | O: Owner, 164 | { 165 | /// Return a reference to the data [`Owner`] provides. 166 | /// 167 | /// # Example 168 | /// ``` 169 | /// use zc::{Zc, Dependant}; 170 | /// 171 | /// #[derive(Debug, PartialEq, Dependant)] 172 | /// struct MyStruct<'a>(&'a [u8]); 173 | /// 174 | /// impl<'a> From<&'a [u8]> for MyStruct<'a> { 175 | /// fn from(bytes: &'a [u8]) -> Self { 176 | /// Self(&bytes[1..]) 177 | /// } 178 | /// } 179 | /// 180 | /// let owner = vec![1, 2, 3]; 181 | /// let data = zc::from!(owner, MyStruct, [u8]); 182 | /// 183 | /// assert_eq!(data.as_owned(), &[1, 2, 3]); 184 | /// ``` 185 | pub fn as_owned(&self) -> &::Target { 186 | &*self.storage 187 | } 188 | 189 | /// Consumes `self` into the [`Owner`]. 190 | /// 191 | /// # Example 192 | /// ``` 193 | /// use zc::{Zc, Dependant}; 194 | /// 195 | /// #[derive(Debug, PartialEq, Dependant)] 196 | /// struct MyStruct<'a>(&'a [u8]); 197 | /// 198 | /// impl<'a> From<&'a [u8]> for MyStruct<'a> { 199 | /// fn from(bytes: &'a [u8]) -> Self { 200 | /// Self(&bytes[1..]) 201 | /// } 202 | /// } 203 | /// 204 | /// let owner = vec![1, 2, 3]; 205 | /// let data = zc::from!(owner, MyStruct, [u8]); 206 | /// 207 | /// assert_eq!(data.into_owner(), vec![1, 2, 3]); 208 | /// ``` 209 | pub fn into_owner(self) -> O { 210 | Owner::from_storage(self.storage) 211 | } 212 | 213 | /// Map the stored [`Dependant`] to another. 214 | /// 215 | /// # Safety 216 | /// 217 | /// The [`Dependant`] passed to the function has its lifetime erased to 218 | /// `'static` and must be handled appropriately. Nothing within the 219 | /// [`Dependant`] passed can be referenced from outside of the closure. 220 | #[inline] 221 | pub unsafe fn map_unchecked(self, f: F) -> Zc 222 | where 223 | F: FnOnce(D) -> U, 224 | { 225 | let Self { value, storage } = self; 226 | let value = f(value); 227 | Zc { value, storage } 228 | } 229 | 230 | /// Try to map the stored [`Dependant`] to another. 231 | /// 232 | /// # Errors 233 | /// 234 | /// Returns any error the provided function returns. 235 | /// 236 | /// # Safety 237 | /// 238 | /// The [`Dependant`] passed to the function has its lifetime erased to 239 | /// `'static` and must be handled appropriately. Nothing within the 240 | /// [`Dependant`] passed can be referenced from outside of the closure, 241 | /// this includes the error returned. 242 | #[inline] 243 | pub unsafe fn try_map_unchecked(self, f: F) -> Result, E> 244 | where 245 | F: FnOnce(D) -> Result, 246 | { 247 | let Self { value, storage } = self; 248 | f(value).map(|value| Zc { value, storage }) 249 | } 250 | } 251 | 252 | impl Zc> 253 | where 254 | O: Owner, 255 | { 256 | /// Decomposes `self` into an option. 257 | #[inline] 258 | pub fn into_option(self) -> Option> { 259 | self.into() 260 | } 261 | } 262 | 263 | impl Zc> 264 | where 265 | O: Owner, 266 | { 267 | /// Decomposes `self` into a result. 268 | /// 269 | /// # Errors 270 | /// 271 | /// Returns `Zc` if the inner dependant is `Result::Err`. 272 | #[inline] 273 | pub fn into_result(self) -> Result, Zc> { 274 | self.into() 275 | } 276 | } 277 | 278 | impl From>> for Option> 279 | where 280 | O: Owner, 281 | { 282 | #[inline] 283 | fn from(zc: Zc>) -> Self { 284 | match zc.value { 285 | None => None, 286 | Some(value) => Some(Zc { 287 | value, 288 | storage: zc.storage, 289 | }), 290 | } 291 | } 292 | } 293 | 294 | impl From>> for Result, Zc> 295 | where 296 | O: Owner, 297 | { 298 | #[inline] 299 | fn from(zc: Zc>) -> Self { 300 | match zc.value { 301 | Ok(value) => Ok(Zc { 302 | value, 303 | storage: zc.storage, 304 | }), 305 | Err(value) => Err(Zc { 306 | value, 307 | storage: zc.storage, 308 | }), 309 | } 310 | } 311 | } 312 | 313 | impl Display for Zc 314 | where 315 | O: Owner, 316 | D: Display, 317 | { 318 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 319 | Display::fmt(&self.value, f) 320 | } 321 | } 322 | 323 | impl Debug for Zc 324 | where 325 | O: Owner, 326 | O::Storage: Debug, 327 | D: Debug, 328 | { 329 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 330 | f.debug_struct("Zc") 331 | .field("storage", &self.storage) 332 | .field("value", &self.value) 333 | .finish() 334 | } 335 | } 336 | 337 | /// Implemented for types that use data provided by an [`Owner`] and guarantee 338 | /// that internal state is protected. 339 | /// 340 | /// # Derive implementations (recommended) 341 | /// 342 | /// It is recommended not to implement this manually and instead use the 343 | /// provided proc-macro as show below. 344 | /// 345 | /// ``` 346 | /// use zc::Dependant; 347 | /// 348 | /// #[derive(Dependant)] 349 | /// pub struct MyStruct<'a> { 350 | /// value: &'a str, 351 | /// } 352 | /// ``` 353 | /// 354 | /// # Derive implementations for `Copy` 355 | /// 356 | /// If a type implements [`Copy`] it cannot support interior mutability and 357 | /// therefore is a valid `Dependant` type. 358 | /// 359 | /// To use a [`Copy`] type without having to implement `Dependant` you can tell 360 | /// the derive implementation to check based on a [`Copy`] bound for a specific 361 | /// field or all fields. 362 | /// 363 | /// ``` 364 | /// use zc::Dependant; 365 | /// 366 | /// #[derive(Copy, Clone)] 367 | /// pub struct CopyType; 368 | /// 369 | /// #[derive(Dependant)] 370 | /// pub struct StructWithCopy<'a> { 371 | /// // This field has a `Copy` bound. 372 | /// #[zc(check = "Copy")] 373 | /// field_a: &'a CopyType, 374 | /// // This field has the standard `Dependant` bound. 375 | /// field_b: u8, 376 | /// } 377 | /// 378 | /// // All fields in this struct have the `Copy` bound. 379 | /// #[derive(Dependant)] 380 | /// #[zc(check = "Copy")] 381 | /// pub struct StructWithAllCopy<'a> { 382 | /// field_a: &'a CopyType, 383 | /// field_b: u8, 384 | /// } 385 | /// ``` 386 | /// 387 | /// # Manual implementations 388 | /// 389 | /// If you wish not to use the provided proc-macro you implement as shown: 390 | /// 391 | /// ``` 392 | /// struct MyStruct<'a>(&'a [u8]); 393 | /// 394 | /// unsafe impl<'o> zc::Dependant<'o> for MyStruct<'o> { 395 | /// type Static = MyStruct<'static>; 396 | /// } 397 | /// ``` 398 | /// 399 | /// # Safety 400 | /// 401 | /// Implementer must guarantee: 402 | /// 403 | /// - the structure only requires a single lifetime. 404 | /// - `Self::Static` must be the same type but with a `'static` lifetime. 405 | /// 406 | /// And in addition the structure: 407 | /// 408 | /// - has no interior mutability. 409 | /// 410 | /// **OR** 411 | /// 412 | /// - can safely be stored with it's lifetime erased (ie. as `'static`). 413 | /// - does not provided an interface that will accept data with non-`'static` 414 | /// lifetime though a interior mutable interface. 415 | /// 416 | /// # Interior Mutability 417 | /// 418 | /// Types that provide interior mutability include both `!Sync` types (eg. 419 | /// [`RefCell`]) and `Sync` types (eg. [`Mutex`]). 420 | /// 421 | /// See the [Rust Language Book] on interior mutability. 422 | /// 423 | /// [`Mutex`]: std::sync::Mutex 424 | /// [`RefCell`]: std::cell::RefCell 425 | /// [Rust Language Book]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html 426 | pub unsafe trait Dependant<'o>: Sized + 'o { 427 | /// Always the exact same structure as `Self` but instead with a `'static` 428 | /// lifetime. 429 | type Static: Dependant<'static>; 430 | } 431 | 432 | /// Represents the owner of data with an associated storage type. 433 | /// 434 | /// An `Owner` is a convenience trait that can be implemented without the need 435 | /// of `unsafe` that returns a [`Storage`] that does require an `unsafe` 436 | /// implementation. See the notes on [`Storage`] to see why this it is required. 437 | pub trait Owner: Sized + 'static { 438 | /// The [`Storage`] type the owner uses. 439 | type Storage: Storage; 440 | 441 | /// Consumes the `Owner` into the associated [`Storage`] type. 442 | fn into_storage(self) -> Self::Storage; 443 | 444 | /// Consumes the associated [`Storage`] into the `Owner` type. 445 | fn from_storage(storage: Self::Storage) -> Self; 446 | } 447 | 448 | impl Owner for T 449 | where 450 | T: Storage, 451 | { 452 | type Storage = T; 453 | 454 | fn into_storage(self) -> Self::Storage { 455 | self 456 | } 457 | 458 | fn from_storage(storage: Self::Storage) -> Self { 459 | storage 460 | } 461 | } 462 | 463 | /// Implemented for types that can safely provide a stable, aliasable reference 464 | /// to data they own. 465 | /// 466 | /// # `noalias` 467 | /// 468 | /// The pointers behind common allocation types (`Box`, `Vec`, etc), are 469 | /// stored via `core::ptr::Unique`, which passes to the compilier a `noalias` 470 | /// attribute. This attribute allows the compiler to make optimisations with the 471 | /// guarantee that no other pointers are referencing the same data. 472 | /// 473 | /// We want to both own the data and provide a reference to it, outside of 474 | /// Rust's normal lifetime guarantees, which can break with some of the 475 | /// optimisations the compiler can make. To achieve this, we need to remove the 476 | /// `noalias` attribute of the underlying pointer to let the compiler know that 477 | /// there will exist multiple pointers referencing the same owned data, which is 478 | /// also known as aliasing. 479 | /// 480 | /// # Safety 481 | /// 482 | /// The implementer must guarantee that the reference it provides via [`Deref`] 483 | /// will be **both stable and aliasable** for the lifetime of `self`. Stable in 484 | /// this context meaning that the pointer to the data referenced will not 485 | /// change. 486 | /// 487 | /// `Box` provides a stable pointer (the location of the data being pointed 488 | /// to will not change) but is not aliasable (see `noalias` above). Instead we 489 | /// can use the basic wrapper types provided by the [`aliasable`] crate. 490 | pub unsafe trait Storage: Sized + Deref + 'static {} 491 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Convenience macro for constructing a [`Zc`] type via a [`Dependant`]'s 2 | /// [`From`]. 3 | /// 4 | /// See [`Zc::new()`] for an example. 5 | /// 6 | /// This macro creates an intermediate function to annotate the lifetime 7 | /// required for the `Construct` trait as the compiler is not smart enough yet 8 | /// to infer it for us. See issues [22340] and [70263]. 9 | /// 10 | /// [22340]: https://github.com/rust-lang/rust/issues/22340 11 | /// [70263]: https://github.com/rust-lang/rust/issues/70263 12 | /// [`Zc`]: crate::Zc 13 | /// [`Zc::new()`]: crate::Zc::new() 14 | /// [`Dependant`]: crate::Dependant 15 | #[macro_export] 16 | macro_rules! from { 17 | ($owner:expr, $dependant:ident, $target:ty) => {{ 18 | fn _new_fn(arg: &$target) -> $dependant<'_> { 19 | $dependant::from(arg) 20 | } 21 | zc::Zc::new($owner, _new_fn) 22 | }}; 23 | } 24 | 25 | /// Convenience macro for constructing a [`Zc`] type via a [`Dependant`]'s 26 | /// [`TryFrom`]. 27 | /// 28 | /// See [`Zc::try_new()`] for an example. 29 | /// 30 | /// This macro creates an intermediate function to annotate the lifetime 31 | /// required for the `TryConstruct` trait as the compiler is not smart enough 32 | /// yet to infer it for us. See issues [22340] and [70263]. 33 | /// 34 | /// [22340]: https://github.com/rust-lang/rust/issues/22340 35 | /// [70263]: https://github.com/rust-lang/rust/issues/70263 36 | /// [`TryFrom`]: core::convert::TryFrom 37 | /// [`Zc`]: crate::Zc 38 | /// [`Zc::try_new()`]: crate::Zc::try_new() 39 | /// [`Dependant`]: crate::Dependant 40 | #[macro_export] 41 | macro_rules! try_from { 42 | ($owner:expr, $dependant:ident, $target:ty) => {{ 43 | fn _new_fn( 44 | arg: &$target, 45 | ) -> Result<$dependant<'_>, <$dependant as core::convert::TryFrom<&$target>>::Error> 46 | { 47 | <$dependant as core::convert::TryFrom<&$target>>::try_from(arg) 48 | } 49 | zc::Zc::try_new($owner, _new_fn) 50 | }}; 51 | } 52 | -------------------------------------------------------------------------------- /src/private.rs: -------------------------------------------------------------------------------- 1 | use core::{mem, ptr}; 2 | 3 | use crate::Dependant; 4 | 5 | unsafe fn erase_lifetime<'o, D: Dependant<'o>>(dependant: D) -> D::Static { 6 | let self_ptr: *const D = &dependant; 7 | let erased = ptr::read(self_ptr.cast::()); 8 | mem::forget(dependant); 9 | erased 10 | } 11 | 12 | pub unsafe trait Construct<'o, O>: Sized 13 | where 14 | O: ?Sized, 15 | { 16 | type Dependant: Dependant<'static>; 17 | 18 | unsafe fn construct(self, owned: &'o O) -> Self::Dependant; 19 | } 20 | 21 | unsafe impl<'o, O, D, F> Construct<'o, O> for F 22 | where 23 | O: ?Sized + 'o, 24 | D: Dependant<'o>, 25 | F: FnOnce(&'o O) -> D + 'static, 26 | { 27 | type Dependant = D::Static; 28 | 29 | unsafe fn construct(self, owned: &'o O) -> Self::Dependant { 30 | erase_lifetime((self)(owned)) 31 | } 32 | } 33 | 34 | pub unsafe trait TryConstruct<'o, O>: Sized 35 | where 36 | O: ?Sized, 37 | { 38 | type Error: 'static; 39 | type Dependant: Dependant<'static>; 40 | 41 | unsafe fn try_construct(self, owned: &'o O) -> Result; 42 | } 43 | 44 | unsafe impl<'o, O, D, E, F> TryConstruct<'o, O> for F 45 | where 46 | E: 'static, 47 | O: ?Sized + 'o, 48 | D: Dependant<'o>, 49 | F: FnOnce(&'o O) -> Result + 'static, 50 | { 51 | type Error = E; 52 | type Dependant = D::Static; 53 | 54 | unsafe fn try_construct(self, owned: &'o O) -> Result { 55 | (self)(owned).map(|d| erase_lifetime(d)) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tarpaulin.toml: -------------------------------------------------------------------------------- 1 | [zc] 2 | features = "alloc, derive" 3 | output-dir = "target/tarpaulin" 4 | exclude-files = ["tests/*", "fuzz/*"] 5 | -------------------------------------------------------------------------------- /tests/invalid-use/dependant_interior_mut.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | use zc::Dependant; 4 | 5 | #[derive(Debug, Dependant)] 6 | pub struct StructWithBytes<'a>(Mutex<&'a [u8]>); 7 | 8 | impl<'a> From<&'a [u8]> for StructWithBytes<'a> { 9 | fn from(bytes: &'a [u8]) -> Self { 10 | Self(Mutex::new(&bytes[1..])) 11 | } 12 | } 13 | 14 | fn main() { 15 | let owner = vec![1, 2, 3]; 16 | let owner2 = vec![6, 6, 6]; 17 | let data = zc::from!(owner, StructWithBytes, [u8]); 18 | dbg!(data.get::()); 19 | *data.get::().0.lock().unwrap() = &owner2; 20 | drop(owner2); 21 | dbg!(data.get::()); 22 | } 23 | -------------------------------------------------------------------------------- /tests/invalid-use/dependant_interior_mut.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `Mutex<&'a [u8]>: Dependant<'a>` is not satisfied 2 | --> $DIR/dependant_interior_mut.rs:6:32 3 | | 4 | 5 | #[derive(Debug, Dependant)] 5 | | --------- required by this bound in `dependant_check` 6 | 6 | pub struct StructWithBytes<'a>(Mutex<&'a [u8]>); 7 | | ^^^^^^^^^^^^^^^ the trait `Dependant<'a>` is not implemented for `Mutex<&'a [u8]>` 8 | -------------------------------------------------------------------------------- /tests/invalid-use/get_dependant_with_static.rs: -------------------------------------------------------------------------------- 1 | use zc::Dependant; 2 | 3 | #[derive(Dependant, PartialEq, Debug)] 4 | pub struct MyStruct<'a>(&'a [u8]); 5 | 6 | impl<'a> From<&'a [u8]> for MyStruct<'a> { 7 | fn from(bytes: &'a [u8]) -> Self { 8 | Self(&bytes) 9 | } 10 | } 11 | 12 | fn main() { 13 | let owner = vec![1, 2, 3]; 14 | let data = zc::from!(owner, MyStruct, [u8]); 15 | let dependant_ref = data.get::>(); 16 | assert_eq!( 17 | dependant_ref, 18 | &MyStruct(&[1, 2, 3]) 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /tests/invalid-use/get_dependant_with_static.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `data` does not live long enough 2 | --> $DIR/get_dependant_with_static.rs:15:25 3 | | 4 | 15 | let dependant_ref = data.get::>(); 5 | | ^^^^--------------------------- 6 | | | 7 | | borrowed value does not live long enough 8 | | argument requires that `data` is borrowed for `'static` 9 | ... 10 | 20 | } 11 | | - `data` dropped here while still borrowed 12 | -------------------------------------------------------------------------------- /tests/invalid-use/non_static_error.rs: -------------------------------------------------------------------------------- 1 | use zc::Dependant; 2 | use core::convert::TryFrom; 3 | 4 | #[derive(Dependant, Debug)] 5 | pub struct MyStruct<'a>(&'a [u8]); 6 | 7 | impl<'a> TryFrom<&'a [u8]> for MyStruct<'a> { 8 | type Error = &'a [u8]; 9 | 10 | fn try_from(bytes: &'a [u8]) -> Result { 11 | match bytes[0] { 12 | 0 => Ok(MyStruct(&bytes[..])), 13 | _ => Err(&bytes[..]), 14 | } 15 | } 16 | } 17 | 18 | fn main() { 19 | let owner = vec![1, 2, 3]; 20 | let result = zc::try_from!(owner, MyStruct, [u8]); 21 | 22 | // should not work 23 | assert_eq!(result.unwrap_err().1, &[1, 2, 3]); 24 | } 25 | -------------------------------------------------------------------------------- /tests/invalid-use/non_static_error.stderr: -------------------------------------------------------------------------------- 1 | error: implementation of `zc::private::TryConstruct` is not general enough 2 | --> $DIR/non_static_error.rs:20:18 3 | | 4 | 20 | let result = zc::try_from!(owner, MyStruct, [u8]); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't satisfy where-clause 6 | | 7 | ::: $WORKSPACE/src/private.rs 8 | | 9 | | / pub unsafe trait TryConstruct<'o, O>: Sized 10 | 35 | | where 11 | 36 | | O: ?Sized, 12 | 37 | | { 13 | ... | 14 | 41 | | unsafe fn try_construct(self, owned: &'o O) -> Result; 15 | 42 | | } 16 | | |_- trait `zc::private::TryConstruct` defined here 17 | | 18 | ::: $WORKSPACE/src/lib.rs 19 | | 20 | | / pub fn try_new(owner: O, constructor: C) -> Result 21 | 110 | | where 22 | 111 | | E: 'static, 23 | 112 | | C: for<'o> TryConstruct<'o, ::Target, Error = E, Dependant = D>, 24 | ... | 25 | 119 | | } 26 | 120 | | } 27 | | |_____- due to a where-clause on `Zc::::try_new`... 28 | | 29 | = note: ...`for<'r> fn(&'r [u8]) -> std::result::Result, as TryFrom<&'r [u8]>>::Error> {_new_fn}` must implement `zc::private::TryConstruct<'0, [u8]>`, for any lifetime `'0`... 30 | = note: ...but `for<'r> fn(&'r [u8]) -> std::result::Result, as TryFrom<&'r [u8]>>::Error> {_new_fn}` actually implements `zc::private::TryConstruct<'1, [u8]>`, for some specific lifetime `'1` 31 | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) 32 | -------------------------------------------------------------------------------- /tests/invalid-use/steal_owned_data.rs: -------------------------------------------------------------------------------- 1 | use zc::{Dependant, Zc}; 2 | use once_cell::sync::OnceCell; 3 | 4 | static THIEF: OnceCell<&'static [u8]> = OnceCell::new(); 5 | 6 | #[derive(Dependant)] 7 | pub struct MyStruct<'a>(&'a [u8]); 8 | 9 | fn steal_owned_data(bytes: &'static [u8]) -> MyStruct<'static> { 10 | THIEF.get_or_init(|| bytes); 11 | MyStruct(bytes) 12 | } 13 | 14 | fn main() { 15 | let owner = vec![1, 2, 3]; 16 | 17 | let _ = Zc::new(owner, steal_owned_data); 18 | 19 | // should not work 20 | assert_eq!(THIEF.get().unwrap(), &[1, 2, 3]); 21 | } 22 | -------------------------------------------------------------------------------- /tests/invalid-use/steal_owned_data.stderr: -------------------------------------------------------------------------------- 1 | error: implementation of `zc::private::Construct` is not general enough 2 | --> $DIR/steal_owned_data.rs:17:13 3 | | 4 | 17 | let _ = Zc::new(owner, steal_owned_data); 5 | | ^^^^^^^ doesn't satisfy where-clause 6 | | 7 | ::: $WORKSPACE/src/private.rs 8 | | 9 | | / pub unsafe trait Construct<'o, O>: Sized 10 | 13 | | where 11 | 14 | | O: ?Sized, 12 | 15 | | { 13 | ... | 14 | 18 | | unsafe fn construct(self, owned: &'o O) -> Self::Dependant; 15 | 19 | | } 16 | | |_- trait `zc::private::Construct` defined here 17 | | 18 | ::: $WORKSPACE/src/lib.rs 19 | | 20 | | / pub fn new(owner: O, constructor: C) -> Self 21 | 74 | | where 22 | 75 | | C: for<'o> Construct<'o, ::Target, Dependant = D>, 23 | 76 | | { 24 | ... | 25 | 81 | | Self { storage, value } 26 | 82 | | } 27 | | |_____- due to a where-clause on `Zc::::new`... 28 | | 29 | = note: ...`fn(&'static [u8]) -> MyStruct<'static> {steal_owned_data}` must implement `zc::private::Construct<'0, [u8]>`, for any lifetime `'0`... 30 | = note: ...but `fn(&'static [u8]) -> MyStruct<'static> {steal_owned_data}` actually implements `zc::private::Construct<'1, [u8]>`, for some specific lifetime `'1` 31 | -------------------------------------------------------------------------------- /tests/invalid-use/todo/non_static_ref_into_f.rs: -------------------------------------------------------------------------------- 1 | use zc::Dependant; 2 | 3 | #[derive(Dependant)] 4 | pub struct MyStruct<'a>(&'a [u8]); 5 | 6 | fn main() { 7 | let owner = vec![1, 2, 3]; 8 | let other = vec![1, 2, 3]; 9 | let _ = zc::new!(owner, |_| MyStruct(&other[..])); 10 | } 11 | -------------------------------------------------------------------------------- /tests/invalid-use/two_lifetimes_on_dependant.rs: -------------------------------------------------------------------------------- 1 | use zc::Dependant; 2 | 3 | #[derive(Dependant)] 4 | pub struct MyStruct<'a, 'b>(&'a (), &'b ()); 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/invalid-use/two_lifetimes_on_dependant.stderr: -------------------------------------------------------------------------------- 1 | error: 2 lifetimes on `MyStruct` when only a single is valid on a `zc::Dependant` 2 | --> $DIR/two_lifetimes_on_dependant.rs:4:20 3 | | 4 | 4 | pub struct MyStruct<'a, 'b>(&'a (), &'b ()); 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/invalid-use/use_dependant_ref_after_drop.rs: -------------------------------------------------------------------------------- 1 | use zc::Dependant; 2 | 3 | #[derive(Dependant, PartialEq, Debug)] 4 | pub struct MyStruct<'a>(&'a [u8]); 5 | 6 | impl<'a> From<&'a [u8]> for MyStruct<'a> { 7 | fn from(bytes: &'a [u8]) -> Self { 8 | Self(&bytes) 9 | } 10 | } 11 | 12 | fn main() { 13 | let owner = vec![1, 2, 3]; 14 | let data = zc::from!(owner, MyStruct, [u8]); 15 | let dependant_ref = data.get::(); 16 | core::mem::drop(data); 17 | assert_eq!( 18 | dependant_ref, 19 | &MyStruct(&[1, 2, 3]) 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /tests/invalid-use/use_dependant_ref_after_drop.stderr: -------------------------------------------------------------------------------- 1 | error[E0505]: cannot move out of `data` because it is borrowed 2 | --> $DIR/use_dependant_ref_after_drop.rs:16:21 3 | | 4 | 15 | let dependant_ref = data.get::(); 5 | | ---- borrow of `data` occurs here 6 | 16 | core::mem::drop(data); 7 | | ^^^^ move out of `data` occurs here 8 | 17 | / assert_eq!( 9 | 18 | | dependant_ref, 10 | 19 | | &MyStruct(&[1, 2, 3]) 11 | 20 | | ) 12 | | |_____- borrow later used here 13 | -------------------------------------------------------------------------------- /tests/test_invalid_use.rs: -------------------------------------------------------------------------------- 1 | #[rustversion::stable] 2 | #[test] 3 | fn invalid_use() { 4 | let t = trybuild::TestCases::new(); 5 | t.compile_fail("tests/invalid-use/*.rs"); 6 | } 7 | -------------------------------------------------------------------------------- /tests/test_zc.rs: -------------------------------------------------------------------------------- 1 | use zc::aliasable::{boxed::AliasableBox, vec::AliasableVec}; 2 | use zc::{Dependant, Zc}; 3 | 4 | #[derive(Dependant)] 5 | pub struct StructWithNoLifetime; 6 | 7 | #[derive(Dependant)] 8 | pub struct ChildType<'a>(&'a ()); 9 | 10 | #[derive(Dependant)] 11 | pub struct StructWithOneLifetime<'a>(ChildType<'a>); 12 | 13 | #[derive(Copy, Clone)] 14 | pub struct CopyType; 15 | 16 | #[derive(Dependant)] 17 | #[allow(dead_code)] 18 | pub struct StructWithCopy<'a> { 19 | #[zc(check = "Copy")] 20 | field_a: &'a CopyType, 21 | field_b: (), 22 | } 23 | 24 | #[derive(Dependant)] 25 | #[allow(dead_code)] 26 | #[zc(check = "Copy")] 27 | pub struct StructWithAllCopy<'a> { 28 | field_a: &'a CopyType, 29 | field_b: (), 30 | } 31 | 32 | #[derive(Dependant)] 33 | #[allow(dead_code)] 34 | pub struct StructWithDefault<'a> { 35 | #[zc(guard = "Default")] 36 | field: &'a (), 37 | } 38 | 39 | #[derive(PartialEq, Debug, Dependant)] 40 | pub struct StructWithBytes<'a>(&'a [u8]); 41 | 42 | impl<'a> From<&'a [u8]> for StructWithBytes<'a> { 43 | fn from(bytes: &'a [u8]) -> Self { 44 | Self(&bytes[1..]) 45 | } 46 | } 47 | 48 | fn construct_struct_with_bytes(bytes: &[u8]) -> StructWithBytes<'_> { 49 | StructWithBytes(&bytes[1..]) 50 | } 51 | 52 | #[test] 53 | fn test_struct_with_bytes_construct() { 54 | let owner = vec![1, 2, 3]; 55 | let data = Zc::new(owner, construct_struct_with_bytes); 56 | 57 | assert_eq!(data.get::(), &StructWithBytes(&[2, 3])); 58 | } 59 | 60 | #[test] 61 | fn test_struct_with_bytes_from() { 62 | let owner = vec![1, 2, 3]; 63 | let data = zc::from!(owner, StructWithBytes, [u8]); 64 | 65 | assert_eq!(data.get::(), &StructWithBytes(&[2, 3])); 66 | 67 | assert_eq!( 68 | format!("{:?}", data), 69 | "Zc { storage: [1, 2, 3], value: StructWithBytes([2, 3]) }" 70 | ); 71 | } 72 | 73 | #[test] 74 | fn test_struct_with_bytes_try_from() { 75 | let owner = vec![1, 2, 3]; 76 | let data = zc::try_from!(owner, StructWithBytes, [u8]).unwrap(); 77 | 78 | assert_eq!(data.get::(), &StructWithBytes(&[2, 3])); 79 | } 80 | 81 | #[test] 82 | fn test_struct_with_str_from() { 83 | #[derive(PartialEq, Debug, Dependant)] 84 | pub struct StructWithStr<'a>(&'a str); 85 | 86 | impl<'a> From<&'a str> for StructWithStr<'a> { 87 | fn from(s: &'a str) -> Self { 88 | Self(&s[1..]) 89 | } 90 | } 91 | 92 | let owner = String::from("hello"); 93 | let data = zc::from!(owner, StructWithStr, str); 94 | 95 | assert_eq!(data.get::(), &StructWithStr("ello")); 96 | } 97 | 98 | #[test] 99 | fn test_struct_with_error() { 100 | #[derive(PartialEq, Debug, Dependant)] 101 | pub struct StructWithError<'a>(&'a str); 102 | 103 | impl<'a> core::convert::TryFrom<&'a str> for StructWithError<'a> { 104 | type Error = (); 105 | 106 | fn try_from(_: &'a str) -> Result { 107 | Err(()) 108 | } 109 | } 110 | 111 | let owner = String::from("hello"); 112 | let result = zc::try_from!(owner, StructWithError, str); 113 | 114 | assert_eq!(result.unwrap_err(), ((), String::from("hello"))); 115 | } 116 | 117 | #[test] 118 | fn test_aliasable_box() { 119 | #[derive(PartialEq, Debug, Dependant)] 120 | pub struct StructWithBoxRef<'a>(&'a u8); 121 | 122 | impl<'a> From<&'a u8> for StructWithBoxRef<'a> { 123 | fn from(v: &'a u8) -> Self { 124 | Self(v) 125 | } 126 | } 127 | 128 | let owner = AliasableBox::from(Box::new(1u8)); 129 | let data = zc::from!(owner, StructWithBoxRef, u8); 130 | 131 | assert_eq!(data.get::(), &StructWithBoxRef(&1)); 132 | assert_eq!(AliasableBox::into_unique(data.into_owner()), Box::new(1)); 133 | } 134 | 135 | #[test] 136 | fn test_aliasable_vec() { 137 | #[derive(PartialEq, Debug, Dependant)] 138 | pub struct StructWithVecRef<'a>(&'a [u8]); 139 | 140 | impl<'a> From<&'a [u8]> for StructWithVecRef<'a> { 141 | fn from(v: &'a [u8]) -> Self { 142 | Self(v) 143 | } 144 | } 145 | 146 | let owner = AliasableVec::from(vec![1u8]); 147 | let data = zc::from!(owner, StructWithVecRef, [u8]); 148 | 149 | assert_eq!(data.get::(), &StructWithVecRef(&[1u8])); 150 | assert_eq!(AliasableVec::into_unique(data.into_owner()), vec![1]); 151 | } 152 | -------------------------------------------------------------------------------- /zc-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zc-derive" 3 | version = "0.4.0" 4 | authors = ["avitex "] 5 | edition = "2018" 6 | description = "Derive macro for zc crate" 7 | categories = ["no-std", "memory-management", "data-structures"] 8 | documentation = "https://docs.rs/zc" 9 | homepage = "https://github.com/avitex/rust-zc" 10 | repository = "https://github.com/avitex/rust-zc" 11 | license = "MIT" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn = "1.0" 18 | quote = "1.0" 19 | proc-macro2 = { version = "1.0", default-features = false } 20 | -------------------------------------------------------------------------------- /zc-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use proc_macro2::{Span, TokenStream as TokenStream2}; 5 | use quote::{quote, quote_spanned}; 6 | use syn::spanned::Spanned; 7 | use syn::{ 8 | parse_macro_input, Attribute, Data, DeriveInput, Field, GenericParam, Ident, Lifetime, 9 | LifetimeDef, 10 | }; 11 | 12 | #[proc_macro_derive(Dependant, attributes(zc))] 13 | pub fn derive_dependant(input: TokenStream) -> TokenStream { 14 | let input = parse_macro_input!(input as DeriveInput); 15 | let name = &input.ident; 16 | let lifetime_count = input.generics.lifetimes().count(); 17 | let derive_opts = match parse_derive_attrs(&input) { 18 | Ok(opts) => opts, 19 | Err(err) => return TokenStream::from(err), 20 | }; 21 | let mut static_generics = input.generics.clone(); 22 | let mut dependant_generics = input.generics.clone(); 23 | let static_lifetime = Lifetime::new("'static", Span::call_site()); 24 | let dependant_lifetime = if lifetime_count == 0 { 25 | let dependant_lifetime = Lifetime::new("'a", Span::call_site()); 26 | dependant_generics.params.insert( 27 | 0, 28 | GenericParam::Lifetime(LifetimeDef::new(dependant_lifetime.clone())), 29 | ); 30 | dependant_lifetime 31 | } else if lifetime_count == 1 { 32 | let first_lifetime_mut = static_generics.lifetimes_mut().next().unwrap(); 33 | let dependant_lifetime = first_lifetime_mut.lifetime.clone(); 34 | first_lifetime_mut.lifetime = static_lifetime; 35 | dependant_lifetime 36 | } else { 37 | let message = format!( 38 | "{} lifetimes on `{}` when only a single is valid on a `zc::Dependant`", 39 | lifetime_count, name 40 | ); 41 | let error = quote_spanned! { input.generics.span() => compile_error!(#message); }; 42 | return TokenStream::from(error); 43 | }; 44 | let field_checks = impl_field_checks(&input, &derive_opts, &dependant_lifetime); 45 | let impl_dependant_generics = dependant_generics.split_for_impl().0; 46 | let ty_generic_static = static_generics.split_for_impl().1; 47 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 48 | let dependant_impl = quote! { 49 | impl #impl_generics #name #ty_generics #where_clause { 50 | fn _zc_field_checks() { 51 | #field_checks 52 | } 53 | } 54 | 55 | unsafe impl #impl_dependant_generics ::zc::Dependant<#dependant_lifetime> for #name #ty_generics #where_clause { 56 | type Static = #name #ty_generic_static; 57 | } 58 | }; 59 | TokenStream::from(dependant_impl) 60 | } 61 | 62 | fn impl_field_checks(input: &DeriveInput, opts: &DeriveOpts, lifetime: &Lifetime) -> TokenStream2 { 63 | match &input.data { 64 | Data::Struct(v) => field_checks(opts, v.fields.iter(), lifetime), 65 | Data::Enum(v) => field_checks( 66 | opts, 67 | v.variants.iter().flat_map(|v| v.fields.iter()), 68 | lifetime, 69 | ), 70 | Data::Union(_) => { 71 | quote_spanned! { input.span() => compile_error!("deriving `zc::Dependant` is not supported for unions"); } 72 | } 73 | } 74 | } 75 | 76 | fn field_checks<'f>( 77 | opts: &DeriveOpts, 78 | fields: impl Iterator, 79 | lifetime: &Lifetime, 80 | ) -> TokenStream2 { 81 | let mut checks = TokenStream2::new(); 82 | checks.extend(quote! { 83 | pub fn copy_check<'a, T: Copy + 'a>() {}; 84 | pub fn dependant_check<'a, T: ::zc::Dependant<'a>>() {}; 85 | }); 86 | for field in fields { 87 | let field_ty = &field.ty; 88 | let field_opts = match parse_field_attrs(opts, field) { 89 | Ok(opts) => opts, 90 | Err(err) => return err, 91 | }; 92 | checks.extend(match field_opts.guard { 93 | CheckType::Copy => quote! { 94 | copy_check::<#lifetime, #field_ty>(); 95 | }, 96 | CheckType::Default => quote! { 97 | dependant_check::<#lifetime, #field_ty>(); 98 | }, 99 | }); 100 | } 101 | checks 102 | } 103 | 104 | #[derive(Copy, Clone)] 105 | enum CheckType { 106 | Copy, 107 | Default, 108 | } 109 | 110 | /////////////////////////////////////////////////////////////////////////////// 111 | // DeriveOpts 112 | 113 | struct DeriveOpts { 114 | check: CheckType, 115 | } 116 | 117 | fn parse_derive_attrs(input: &DeriveInput) -> Result { 118 | let zc_attr_ident = Ident::new("zc", Span::call_site()); 119 | let zc_attrs = input 120 | .attrs 121 | .iter() 122 | .filter(|attr| attr.path.get_ident() == Some(&zc_attr_ident)); 123 | 124 | let mut attrs = DeriveOpts { 125 | check: CheckType::Default, 126 | }; 127 | 128 | for attr in zc_attrs { 129 | let attr_value = attr.tokens.to_string(); 130 | 131 | attrs.check = parse_guard_type(&attr, attr_value.as_str())?; 132 | } 133 | 134 | Ok(attrs) 135 | } 136 | 137 | /////////////////////////////////////////////////////////////////////////////// 138 | // FieldOpts 139 | 140 | struct FieldOpts { 141 | guard: CheckType, 142 | } 143 | 144 | fn parse_field_attrs(opts: &DeriveOpts, input: &Field) -> Result { 145 | let zc_attr_ident = Ident::new("zc", Span::call_site()); 146 | let zc_attrs = input 147 | .attrs 148 | .iter() 149 | .filter(|attr| attr.path.get_ident() == Some(&zc_attr_ident)); 150 | 151 | let mut attrs = FieldOpts { guard: opts.check }; 152 | 153 | for attr in zc_attrs { 154 | attrs.guard = parse_guard_type(&attr, attr.tokens.to_string().as_str())?; 155 | } 156 | 157 | Ok(attrs) 158 | } 159 | 160 | fn parse_guard_type(attr: &Attribute, attr_value: &str) -> Result { 161 | match attr_value { 162 | r#"(check = "Copy")"# => Ok(CheckType::Copy), 163 | r#"(guard = "Default")"# => Ok(CheckType::Default), 164 | _ => Err(quote_spanned! { attr.span() => compile_error!("Unknown `zc` options"); }), 165 | } 166 | } 167 | --------------------------------------------------------------------------------