diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2022-05-07 12:18:38 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2022-05-07 12:18:38 -0300 |
commit | f21e8e1bea1e93aaa22e4d6baa3a490140977617 (patch) | |
tree | 3ea65a5e9ec5d4c1f59d9f813226b65f010e7f5c /src/lib.rs |
[Project] LtPtr
"Checked" raw pointers for Rust.
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0b1837d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,155 @@ +#![cfg_attr(not(feature="std"),no_std)] + +//! This crate provides "checked" raw pointers. +//! +//! We found ourselves to be experiencing a lot of cognitive overhead when +//! doing FFI interactions. A non-insignificant portion of them came from raw +//! pointers, `as_ptr`, `as_mut_ptr`, and the like. This removes that cognitive +//! overhead. + +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::ops::Range; + +/// The actual impls. +mod impls; + +/// `*const T` with a lifetime. +/// +/// # Examples +/// +/// Rust doesn't catch incorrect `as_ptr` usage (tho there is a lint for it): +/// +/// ```no_run +/// use std::ffi::{CString, CStr}; +/// use std::os::raw::c_char; +/// +/// extern "C" { +/// fn my_ffi_function(data: *const c_char); +/// } +/// let str = CString::new("world!").unwrap().as_ptr(); +/// // oops! CString dropped here! ^ +/// unsafe { my_ffi_function(str) } // crashes at runtime +/// ``` +/// +/// but we can use a `ConstLtPtr` to make it do so: +/// +/// ```compile_fail +/// use std::ffi::{CString, CStr}; +/// use std::os::raw::c_char; +/// use ltptr::*; +/// +/// extern "C" { +/// fn my_ffi_function(data: ConstLtPtr<'_, c_char>); +/// } +/// let str = CString::new("world!").unwrap().as_lt_ptr(); +/// unsafe { my_ffi_function(str) } +/// // error[E0716]: temporary value dropped while borrowed +/// ``` +/// +/// which we can then fix: +/// +/// ```no_run +/// use std::ffi::{CString, CStr}; +/// use std::os::raw::c_char; +/// use ltptr::*; +/// +/// extern "C" { +/// fn my_ffi_function(data: ConstLtPtr<'_, c_char>); +/// } +/// let str = CString::new("world!").unwrap(); +/// unsafe { my_ffi_function(str.as_lt_ptr()) } +/// ``` +/// +/// +/// and if we call the wrong thing, it fails to compile: +/// +/// ```compile_fail +/// use std::ffi::{CString, CStr}; +/// use std::os::raw::c_char; +/// use ltptr::*; +/// +/// extern "C" { +/// fn my_ffi_function(data: ConstLtPtr<'_, c_char>); +/// } +/// let str = CString::new("world!").unwrap(); +/// unsafe { my_ffi_function(str.as_ptr()) } // should be as_lt_ptr! +/// ``` +#[repr(transparent)] +pub struct ConstLtPtr<'a, T: ?Sized> { + raw: *const T, + _lt: PhantomData<&'a T>, +} + +/// `*mut T` with a lifetime. +#[repr(transparent)] +pub struct MutLtPtr<'a, T: ?Sized> { + raw: *mut T, + _lt: PhantomData<&'a UnsafeCell<T>>, +} + +/// Trait for conversion into a [`ConstLtPtr`]. +pub trait AsLtPtr { + type Target: ?Sized; + /// Returns a pointer as if by `as_ptr` on a type that implements this, but + /// with a bound lifetime. + fn as_lt_ptr<'a>(&'a self) -> ConstLtPtr<'a, Self::Target>; +} + +/// Trait for conversion into a [`MutLtPtr`]. +pub trait AsMutLtPtr: AsLtPtr { + /// Returns a pointer as if by `as_ptr` on a type that implements this, but + /// with a bound lifetime. + fn as_mut_lt_ptr<'a>(&'a mut self) -> MutLtPtr<'a, Self::Target>; +} + +/// Additional slice methods. +pub trait SliceExt<T>: impls::slice::Sealed { + /// Returns the two raw pointers spanning the slice. + /// + /// See [`[T]::as_ptr_range`] for extended documentation. + /// + /// The pointers returned by this function have bound lifetimes. + fn as_lt_ptr_range<'a>(&'a self) -> Range<ConstLtPtr<'a, T>>; + + /// Returns the two unsafe mutable pointers spanning the slice. + /// + /// See [`[T]::as_mut_ptr_range`] for extended documentation. + /// + /// The pointers returned by this function have bound lifetimes. + fn as_mut_lt_ptr_range<'a>(&'a mut self) -> Range<MutLtPtr<'a, T>>; +} + +impl<'a, T: ?Sized> ConstLtPtr<'a, T> { + /// Creates a new `ConstLtPtr` from the given raw pointer. + /// + /// # Safety + /// + /// This function is unsafe as an advisory: the returned `ConstLtPtr` has + /// an arbitrary lifetime `'a`, so using this function directly doesn't + /// help reduce cognitive overhead. + #[inline] + pub const unsafe fn from_raw(raw: *const T) -> Self { + Self { + raw: raw, + _lt: PhantomData, + } + } +} + +impl<'a, T: ?Sized> MutLtPtr<'a, T> { + /// Creates a new `MutLtPtr` from the given raw pointer. + /// + /// # Safety + /// + /// This function is unsafe as an advisory: the returned `MutLtPtr` has + /// an arbitrary lifetime `'a`, so using this function directly doesn't + /// help reduce cognitive overhead. + #[inline] + pub const unsafe fn from_raw(raw: *mut T) -> Self { + Self { + raw: raw, + _lt: PhantomData, + } + } +} |