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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#![forbid(missing_docs)]
// #![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(core_intrinsics, coerce_unsized, unsize, maybe_uninit))]

/*!
 * This crate brings a safe out reference to Rust
 * 
 *  See readme for more info
 */

#[cfg(not(feature = "std"))]
extern crate core as std;

#[cfg(test)]
mod tests;

#[cfg(feature = "nightly")]
use std::mem::MaybeUninit;
use std::marker::PhantomData as PD;
/**
 * An Out Reference, you can only write to this reference using the `set` method
 * and reborrow this reference with the `borrow` method
 */
#[derive(Debug)]
#[repr(transparent)]
pub struct Out<'a, T: ?Sized>(*mut T, PD<&'a mut T>);

impl<'a, T> Out<'a, T> {
    /// To allow writing to the value inside the MaybeUninit
    #[cfg(feature = "nightly")]
    pub fn from_maybe_uninit(maybe_uninit: &mut MaybeUninit<T>) -> Out<'_, T> {
        Out(maybe_uninit.as_mut_ptr(), PD)
    }
}

impl<'a, T: ?Sized> Out<'a, T> {
    /// Create Out from raw ptr
    /// 
    /// Note: will panic if the raw pointer is null
    pub fn from_raw(ptr: *mut T) -> Out<'a, T> {
        if ptr.is_null() {
            panic!("Tried to create a Out reference from a raw pointer")
        }
        Out(ptr, PD)
    }
    /// Create Out from raw ptr
    /// 
    /// Note: the raw pointer must not be null
    pub unsafe fn from_raw_unchecked(ptr: *mut T) -> Out<'a, T> {
        Out(ptr, PD)
    }

    /// Reborrows the `Out` reference
    pub fn borrow(&mut self) -> Out<'_, T> { Out(self.0, PD) }

    /// Convert this `Out` reference into a raw pointer
    pub fn into_raw(self) -> *mut T { self.0 }
}

impl<'a, T> Out<'a, T> {
    /// Set the value behind the `Out` reference without dropping the old value 
    pub fn set(&mut self, value: T) { unsafe { std::ptr::write(self.0, value) } }
}

/// Used to create an Out reference, for all types
pub trait OutMethod {
    /// creates an Out ref
    #[inline(always)]
    fn out(&mut self) -> Out<'_, Self> { Out(self, PD) }

    /// creates an LinearOut ref
    #[inline(always)]
    #[cfg(any(feature = "std", feature = "nightly"))]
    fn linear_out(&mut self) -> LinearOut<'_, Self> { OutMethod::out(self).into_linear() }
}

impl<T: ?Sized> OutMethod for T {}

impl<'a, T: ?Sized> From<&'a mut T> for Out<'a, T> {
    fn from(ptr: &'a mut T) -> Self {
        Out(ptr, PD)
    }
}

#[cfg(any(feature = "std", feature = "nightly"))]
pub use self::linear::*;

#[cfg(any(feature = "std", feature = "nightly"))]
mod linear {
    use super::*;

    /// `LienarOut` represents a linear type that must be written to
    /// exactly once, if this type is not written to it will ***abort the process***
    pub struct LinearOut<'a, T: ?Sized>(Out<'a, T>);

    impl<'a, T> LinearOut<'a, T> {
        /// Set a value to the linear type
        pub fn set(mut self, value: T) {
            self.0.set(value);
            std::mem::forget(self)
        }
    }

    impl<'a, T: ?Sized> Drop for LinearOut<'a, T> {
        fn drop(&mut self) {
            #[cfg(feature = "std")]
            std::process::abort();

            #[cfg(all(
                feature = "nightly",
                not(feature = "std")
            ))]
            unsafe { std::intrinsics::abort(); }
        }
    }

    impl<'a, T: ?Sized> Out<'a, T> {
        /// Convert an `Out` reference into a `LinearOut` reference
        pub fn into_linear(self) -> LinearOut<'a, T> {
            LinearOut(self)
        }
    }

    #[cfg(feature = "nightly")]
    pub use self::nightly::*;

    #[cfg(feature = "nightly")]
    mod nightly {
        use std::ops::CoerceUnsized;
        use std::marker::Unsize;
        use super::*;

        impl<'a, T: Unsize<U>, U: ?Sized> CoerceUnsized<Out<'a, U>> for Out<'a, T> {}
    }
}