ltptr/
lib.rs

1// Copyright (C) 2022 Soni L.
2// SPDX-License-Identifier: 0BSD
3
4#![cfg_attr(not(feature="std"),no_std)]
5#![deny(unsafe_op_in_unsafe_fn)]
6
7//! This crate provides "checked" raw pointers.
8//!
9//! We found ourselves to be experiencing a lot of cognitive overhead when
10//! doing FFI interactions. A non-insignificant portion of them came from raw
11//! pointers, `as_ptr`, `as_mut_ptr`, and the like. This removes that cognitive
12//! overhead.
13//!
14//! And as [Amos once said], "Until we demand better of our tools, we are
15//! doomed to be woken up in the middle of the night, over and over again,
16//! because some nil value slipped in where it never should have.". That is, we
17//! are doomed to repeat the same mistakes over and over again. So we must
18//! demand better of our tools!
19//!
20//! [Amos once said]: https://fasterthanli.me/articles/lies-we-tell-ourselves-to-keep-using-golang
21
22use core::cell::UnsafeCell;
23use core::marker::PhantomData;
24use core::ops::Range;
25
26/// The actual impls.
27mod impls;
28
29/// `*const T` with a lifetime.
30///
31/// # Examples
32///
33/// Rust doesn't catch incorrect `as_ptr` usage (tho there is a lint for it):
34///
35/// ```no_run
36/// use std::ffi::{CString, CStr};
37/// use std::os::raw::c_char;
38///
39/// extern "C" {
40///     fn my_ffi_function(data: *const c_char);
41/// }
42/// let str = CString::new("world!").unwrap().as_ptr();
43/// //                    oops! CString dropped here! ^
44/// unsafe { my_ffi_function(str) } // crashes at runtime
45/// ```
46///
47/// but we can use a `ConstLtPtr` to make it do so:
48///
49/// ```compile_fail
50/// use std::ffi::{CString, CStr};
51/// use std::os::raw::c_char;
52/// use ltptr::*;
53///
54/// extern "C" {
55///     fn my_ffi_function(data: ConstLtPtr<'_, c_char>);
56/// }
57/// let str = CString::new("world!").unwrap().as_lt_ptr();
58/// unsafe { my_ffi_function(str) }
59/// // error[E0716]: temporary value dropped while borrowed
60/// ```
61///
62/// which we can then fix:
63///
64/// ```no_run
65/// use std::ffi::{CString, CStr};
66/// use std::os::raw::c_char;
67/// use ltptr::*;
68///
69/// extern "C" {
70///     fn my_ffi_function(data: ConstLtPtr<'_, c_char>);
71/// }
72/// let str = CString::new("world!").unwrap();
73/// unsafe { my_ffi_function(str.as_lt_ptr()) }
74/// ```
75///
76///
77/// and if we call the wrong thing, it fails to compile:
78///
79/// ```compile_fail
80/// use std::ffi::{CString, CStr};
81/// use std::os::raw::c_char;
82/// use ltptr::*;
83///
84/// extern "C" {
85///     fn my_ffi_function(data: ConstLtPtr<'_, c_char>);
86/// }
87/// let str = CString::new("world!").unwrap();
88/// unsafe { my_ffi_function(str.as_ptr()) } // should be as_lt_ptr!
89/// ```
90#[repr(transparent)]
91pub struct ConstLtPtr<'a, T: ?Sized> {
92  raw: *const T,
93  _lt: PhantomData<&'a T>,
94}
95
96/// `*mut T` with a lifetime.
97#[repr(transparent)]
98pub struct MutLtPtr<'a, T: ?Sized> {
99  raw: *mut T,
100  _lt: PhantomData<&'a UnsafeCell<T>>,
101}
102
103/// Trait for conversion into a [`ConstLtPtr`].
104pub trait AsLtPtr {
105    type Target: ?Sized;
106    /// Returns a pointer as if by `as_ptr` on a type that implements this, but
107    /// with a bound lifetime.
108    fn as_lt_ptr<'a>(&'a self) -> ConstLtPtr<'a, Self::Target>;
109}
110
111/// Trait for conversion from a [`ConstLtPtr`] of the given `Source` type.
112pub trait FromLtPtr<Source> {
113    /// Returns a value as if by `from_ptr` on a type that implements this, but
114    /// with a bound lifetime.
115    unsafe fn from_lt_ptr<'a>(ptr: ConstLtPtr<'a, Source>) -> &'a Self;
116}
117
118/// Trait for conversion into a [`MutLtPtr`].
119pub trait AsMutLtPtr: AsLtPtr {
120    /// Returns a pointer as if by `as_ptr` on a type that implements this, but
121    /// with a bound lifetime.
122    fn as_mut_lt_ptr<'a>(&'a mut self) -> MutLtPtr<'a, Self::Target>;
123}
124
125/// Additional slice methods.
126pub trait SliceExt<T>: impls::slice::Sealed {
127    /// Returns the two raw pointers spanning the slice.
128    ///
129    /// See [`slice::as_ptr_range`] for extended documentation.
130    ///
131    /// The pointers returned by this function have bound lifetimes.
132    fn as_lt_ptr_range<'a>(&'a self) -> Range<ConstLtPtr<'a, T>>;
133
134    /// Returns the two unsafe mutable pointers spanning the slice.
135    ///
136    /// See [`slice::as_mut_ptr_range`] for extended documentation.
137    ///
138    /// The pointers returned by this function have bound lifetimes.
139    fn as_mut_lt_ptr_range<'a>(&'a mut self) -> Range<MutLtPtr<'a, T>>;
140}
141
142impl<'a, T: ?Sized> ConstLtPtr<'a, T> {
143    /// Creates a new `ConstLtPtr` from the given raw pointer.
144    ///
145    /// # Safety
146    ///
147    /// This function is unsafe as an advisory: the returned `ConstLtPtr` has
148    /// an arbitrary lifetime `'a`, so using this function directly doesn't
149    /// help reduce cognitive overhead.
150    #[inline]
151    pub const unsafe fn from_raw(raw: *const T) -> Self {
152        Self {
153            raw: raw,
154            _lt: PhantomData,
155        }
156    }
157
158    /// Returns this `ConstLtPtr` as a raw pointer.
159    ///
160    /// # Safety
161    ///
162    /// This function is unsafe as an advisory: the returned raw pointer loses
163    /// the lifetime.
164    #[inline]
165    pub const unsafe fn as_raw(&self) -> *const T {
166        self.raw
167    }
168
169    /// Checks if this pointer is null.
170    ///
171    /// # Examples
172    ///
173    /// A null pointer is null:
174    ///
175    /// ```rust
176    /// use ltptr::ConstLtPtr;
177    ///
178    /// let raw_ptr = core::ptr::null::<()>();
179    /// let lt_ptr = unsafe { ConstLtPtr::from_raw(raw_ptr) };
180    /// assert!(lt_ptr.is_null());
181    /// ```
182    #[inline]
183    pub fn is_null(self) -> bool {
184        self.raw.is_null()
185    }
186}
187
188impl<'a, T: ?Sized> MutLtPtr<'a, T> {
189    /// Creates a new `MutLtPtr` from the given raw pointer.
190    ///
191    /// # Safety
192    ///
193    /// This function is unsafe as an advisory: the returned `MutLtPtr` has
194    /// an arbitrary lifetime `'a`, so using this function directly doesn't
195    /// help reduce cognitive overhead.
196    #[inline]
197    pub const unsafe fn from_raw(raw: *mut T) -> Self {
198        Self {
199            raw: raw,
200            _lt: PhantomData,
201        }
202    }
203
204    /// Returns this `MutLtPtr` as a raw pointer.
205    ///
206    /// # Safety
207    ///
208    /// This function is unsafe as an advisory: the returned raw pointer loses
209    /// the lifetime.
210    #[inline]
211    pub const unsafe fn as_raw(&self) -> *mut T {
212        self.raw
213    }
214
215    /// Checks if this pointer is null.
216    /// # Examples
217    ///
218    /// A null pointer is null:
219    ///
220    /// ```rust
221    /// use ltptr::MutLtPtr;
222    ///
223    /// let raw_ptr = core::ptr::null_mut::<()>();
224    /// let lt_ptr = unsafe { MutLtPtr::from_raw(raw_ptr) };
225    /// assert!(lt_ptr.is_null());
226    /// ```
227    #[inline]
228    pub fn is_null(self) -> bool {
229        self.raw.is_null()
230    }
231}