#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#[doc(inline)]
pub use ::vptr_macros::vptr;
use core::borrow::{Borrow, BorrowMut};
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
use core::ptr::NonNull;
#[cfg(feature = "std")]
use std::boxed::Box;
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd)]
pub struct VPtr<T, Trait: ?Sized>
where
T: HasVPtr<Trait>,
{
vtable: &'static VTableData,
phantom: PhantomData<(*const T, *const Trait)>,
}
impl<T, Trait: ?Sized> VPtr<T, Trait>
where
T: HasVPtr<Trait>,
{
pub fn new() -> Self {
VPtr {
vtable: T::init(),
phantom: PhantomData,
}
}
}
impl<T, Trait: ?Sized> Default for VPtr<T, Trait>
where
T: HasVPtr<Trait>,
{
fn default() -> Self {
VPtr::new()
}
}
#[cfg(feature = "std")]
impl<T, Trait: ?Sized> std::fmt::Debug for VPtr<T, Trait>
where
T: HasVPtr<Trait>,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.pad("VPtr")
}
}
pub unsafe trait HasVPtr<Trait: ?Sized> {
fn init() -> &'static VTableData;
fn get_vptr(&self) -> &VPtr<Self, Trait>
where
Self: Sized;
fn get_vptr_mut(&mut self) -> &mut VPtr<Self, Trait>
where
Self: Sized;
fn as_thin_ref(&self) -> ThinRef<Trait>
where
Self: Sized,
{
unsafe { ThinRef::new(self.get_vptr()) }
}
fn as_thin_ref_mut(&mut self) -> ThinRefMut<Trait>
where
Self: Sized,
{
unsafe { ThinRefMut::new(self.get_vptr_mut()) }
}
fn as_pin_thin_ref(self: Pin<&Self>) -> Pin<ThinRef<Trait>>
where
Self: Sized,
{
unsafe { Pin::new_unchecked(self.get_ref().as_thin_ref()) }
}
fn as_pin_thin_ref_mut(self: Pin<&mut Self>) -> Pin<ThinRefMut<Trait>>
where
Self: Sized,
{
unsafe { Pin::new_unchecked(self.get_unchecked_mut().as_thin_ref_mut()) }
}
}
pub struct ThinRef<'a, Trait: ?Sized> {
ptr: &'a &'static VTableData,
phantom: PhantomData<&'a Trait>,
}
impl<'a, Trait: ?Sized> ThinRef<'a, Trait> {
unsafe fn new<T: HasVPtr<Trait>>(ptr: &'a VPtr<T, Trait>) -> Self {
ThinRef {
ptr: &ptr.vtable,
phantom: PhantomData,
}
}
}
impl<'a, Trait: ?Sized + 'a> Deref for ThinRef<'a, Trait> {
type Target = Trait;
fn deref(&self) -> &Self::Target {
unsafe {
let VTableData { offset, vtable } = **self.ptr;
let p = (self.ptr as *const _ as *const u8).offset(-offset) as *const ();
internal::TransmuterTO::<Trait> {
to: internal::TraitObject { data: p, vtable },
}
.ptr
}
}
}
impl<'a, Trait: ?Sized + 'a> Borrow<Trait> for ThinRef<'a, Trait> {
fn borrow(&self) -> &Trait {
&**self
}
}
impl<'a, Trait: ?Sized + 'a, T: HasVPtr<Trait>> From<&'a T> for ThinRef<'a, Trait> {
fn from(f: &'a T) -> Self {
unsafe { ThinRef::new(f.get_vptr()) }
}
}
impl<'a, Trait: ?Sized> Clone for ThinRef<'a, Trait> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, Trait: ?Sized> Copy for ThinRef<'a, Trait> {}
pub struct ThinRefMut<'a, Trait: ?Sized> {
ptr: &'a mut &'static VTableData,
phantom: PhantomData<&'a mut Trait>,
}
impl<'a, Trait: ?Sized> ThinRefMut<'a, Trait> {
unsafe fn new<T: HasVPtr<Trait>>(ptr: &'a mut VPtr<T, Trait>) -> Self {
ThinRefMut {
ptr: &mut ptr.vtable,
phantom: PhantomData,
}
}
}
impl<'a, Trait: ?Sized + 'a> Deref for ThinRefMut<'a, Trait> {
type Target = Trait;
fn deref(&self) -> &Self::Target {
unsafe {
let VTableData { offset, vtable } = **self.ptr;
let p = (self.ptr as *const _ as *const u8).offset(-offset) as *const ();
internal::TransmuterTO::<Trait> {
to: internal::TraitObject { data: p, vtable },
}
.ptr
}
}
}
impl<'a, Trait: ?Sized + 'a> DerefMut for ThinRefMut<'a, Trait> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
let VTableData { offset, vtable } = **self.ptr;
let p = (self.ptr as *mut _ as *mut u8).offset(-offset) as *mut ();
union Transmuter<T: ?Sized> {
pub ptr: *mut T,
pub to: internal::TraitObject,
}
let ptr = Transmuter::<Trait> {
to: internal::TraitObject { data: p, vtable },
}
.ptr;
&mut *ptr
}
}
}
impl<'a, Trait: ?Sized + 'a> Borrow<Trait> for ThinRefMut<'a, Trait> {
fn borrow(&self) -> &Trait {
&**self
}
}
impl<'a, Trait: ?Sized + 'a> BorrowMut<Trait> for ThinRefMut<'a, Trait> {
fn borrow_mut(&mut self) -> &mut Trait {
&mut **self
}
}
impl<'a, Trait: ?Sized + 'a, T: HasVPtr<Trait>> From<&'a mut T> for ThinRefMut<'a, Trait> {
fn from(f: &'a mut T) -> Self {
unsafe { ThinRefMut::new(f.get_vptr_mut()) }
}
}
#[cfg(feature = "std")]
#[repr(transparent)]
pub struct ThinBox<Trait: ?Sized + 'static>(NonNull<&'static VTableData>, PhantomData<*mut Trait>);
#[cfg(feature = "std")]
#[allow(clippy::wrong_self_convention)]
impl<Trait: ?Sized + 'static> ThinBox<Trait> {
pub fn from_box<T: HasVPtr<Trait>>(f: Box<T>) -> Self {
ThinBox(
NonNull::from(&mut Box::leak(f).get_vptr_mut().vtable),
PhantomData,
)
}
pub fn into_box(mut b: ThinBox<Trait>) -> Box<Trait> {
let ptr = (&mut *ThinBox::as_thin_ref_mut(&mut b)) as *mut Trait;
core::mem::forget(b);
unsafe { Box::from_raw(ptr) }
}
pub fn as_thin_ref(b: &ThinBox<Trait>) -> ThinRef<Trait> {
ThinRef {
ptr: unsafe { b.0.as_ref() },
phantom: PhantomData,
}
}
pub fn as_thin_ref_mut(b: &mut ThinBox<Trait>) -> ThinRefMut<Trait> {
ThinRefMut {
ptr: unsafe { b.0.as_mut() },
phantom: PhantomData,
}
}
}
#[cfg(feature = "std")]
impl<Trait: ?Sized + 'static> Drop for ThinBox<Trait> {
fn drop(&mut self) {
let ptr = &mut *ThinBox::as_thin_ref_mut(self) as *mut Trait;
unsafe { Box::from_raw(ptr) };
}
}
#[cfg(feature = "std")]
impl<Trait: ?Sized + 'static> Deref for ThinBox<Trait> {
type Target = Trait;
fn deref(&self) -> &Self::Target {
let ptr = &*ThinBox::as_thin_ref(self) as *const Trait;
unsafe { &*ptr }
}
}
#[cfg(feature = "std")]
impl<Trait: ?Sized + 'static> DerefMut for ThinBox<Trait> {
fn deref_mut(&mut self) -> &mut Self::Target {
let ptr = &mut *ThinBox::as_thin_ref_mut(self) as *mut Trait;
unsafe { &mut *ptr }
}
}
#[derive(Eq, Hash, PartialEq, PartialOrd)]
pub struct VTableData {
pub offset: isize,
pub vtable: *const (),
}
unsafe impl core::marker::Sync for VTableData {}
pub mod prelude {
#[doc(no_inline)]
pub use crate::{vptr, HasVPtr};
}
#[doc(hidden)]
pub mod internal {
#[doc(hidden)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct TraitObject {
pub data: *const (),
pub vtable: *const (),
}
#[doc(hidden)]
pub union TransmuterPtr<T: 'static> {
pub ptr: &'static T,
pub int: isize,
}
#[doc(hidden)]
pub union TransmuterTO<'a, T: ?Sized + 'a> {
pub ptr: &'a T,
pub to: TraitObject,
}
}
#[cfg(test)]
mod tests {
pub use crate::{vptr, HasVPtr, ThinBox, ThinRef, ThinRefMut, VPtr};
mod vptr {
pub use crate::*;
}
trait MyTrait {
fn myfn(&self) -> u32;
}
#[vptr(MyTrait)]
#[derive(Default)]
struct Foobar2 {
q: u32,
}
impl MyTrait for Foobar2 {
fn myfn(&self) -> u32 {
self.q + 4
}
}
#[test]
fn it_works2() {
let mut f = Foobar2::default();
f.q = 5;
assert_eq!(f.myfn(), 9);
let xx = f.as_thin_ref();
assert_eq!(xx.myfn(), 9);
}
#[vptr(MyTrait, SomeOtherTrait)]
#[derive(Default, Debug)]
struct Foobar3 {
q: u32,
}
impl MyTrait for Foobar3 {
fn myfn(&self) -> u32 {
self.q + 4
}
}
trait SomeOtherTrait {}
impl SomeOtherTrait for Foobar3 {}
#[test]
fn it_works3() {
let mut f = Foobar3::default();
f.q = 5;
println!("{:?}", f);
assert_eq!(f.myfn(), 9);
{
let xx: ThinRef<dyn MyTrait> = f.as_thin_ref();
assert_eq!(xx.myfn(), 9);
}
{
let xx: ThinRefMut<dyn MyTrait> = f.as_thin_ref_mut();
assert_eq!(xx.myfn(), 9);
}
}
#[vptr(MyTrait)]
#[derive(Default)]
struct WithLifeTime<'a> {
pub foo: Option<&'a u32>,
}
impl<'a> MyTrait for WithLifeTime<'a> {
fn myfn(&self) -> u32 {
*self.foo.unwrap_or(&0)
}
}
#[test]
fn with_lifetime() {
let mut f = WithLifeTime::default();
let x = 43;
f.foo = Some(&x);
assert_eq!(f.myfn(), 43);
let xx: ThinRef<dyn MyTrait> = f.as_thin_ref();
assert_eq!(xx.myfn(), 43);
}
#[vptr(MyTrait)]
struct Tuple(u32, u32);
impl MyTrait for Tuple {
fn myfn(&self) -> u32 {
self.1
}
}
#[test]
fn tuple() {
let f = Tuple(42, 43, Default::default());
assert_eq!(f.myfn(), 43);
let xx: ThinRef<_> = f.as_thin_ref();
assert_eq!(xx.myfn(), 43);
}
#[vptr(MyTrait)]
struct Empty1;
impl MyTrait for Empty1 {
fn myfn(&self) -> u32 {
88
}
}
#[test]
fn empty_struct() {
let f = Empty1(VPtr::new());
assert_eq!(f.myfn(), 88);
let xx: ThinRef<dyn MyTrait> = f.as_thin_ref();
assert_eq!(xx.myfn(), 88);
}
#[vptr(std::fmt::Display)]
struct TestDisplay {
str: String,
}
impl std::fmt::Display for TestDisplay {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(fmt, "Test {}", self.str)
}
}
#[test]
fn with_path() {
let x = TestDisplay {
str: "Hello".to_string(),
vptr_Display: Default::default(),
};
let xx: ThinRef<dyn std::fmt::Display> = x.as_thin_ref();
assert_eq!(xx.to_string(), "Test Hello");
}
#[test]
fn test_trait_with_gen() {
trait TraitWithGen<T> {
fn compute(&self, x: T) -> u32;
}
#[vptr("TraitWithGen<u64>")]
struct TestTraitWithGen {
value: u32,
}
impl TraitWithGen<u64> for TestTraitWithGen {
fn compute(&self, x: u64) -> u32 {
self.value + (x as u32)
}
}
let x = TestTraitWithGen {
value: 44,
vptr_TraitWithGen: Default::default(),
};
let xx: ThinRef<dyn TraitWithGen<u64>> = x.as_thin_ref();
assert_eq!(core::mem::size_of_val(&xx), core::mem::size_of::<usize>());
assert_eq!(xx.compute(66u64), 44 + 66);
}
#[test]
fn copy() {
let f = Tuple(2, 3, Default::default());
let xx: ThinRef<_> = f.as_thin_ref();
let xx2 = xx;
assert_eq!(xx.myfn(), 3);
assert_eq!(xx2.myfn(), 3);
}
#[test]
fn pin() {
use core::pin::Pin;
{
let mut f = Foobar3::default();
f.q = 5;
let f: Pin<&Foobar3> = unsafe { Pin::new_unchecked(&f) };
let xx: Pin<ThinRef<dyn MyTrait>> = f.as_pin_thin_ref();
assert_eq!(xx.myfn(), 9);
}
{
let mut f = Foobar3::default();
f.q = 8;
let f: Pin<&mut Foobar3> = unsafe { Pin::new_unchecked(&mut f) };
let xx: Pin<ThinRefMut<dyn MyTrait>> = f.as_pin_thin_ref_mut();
assert_eq!(xx.myfn(), 12);
}
}
}