• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 20059345204

09 Dec 2025 09:57AM UTC coverage: 58.683% (-0.4%) from 59.082%
20059345204

push

github

fasterthanlime
Refactor VTable system: Direct/Indirect styles, OxRef/OxMut, builder patterns

Major architectural refactor of the facet vtable system for better code sharing
and reduced binary bloat.

- Remove ThinPtr/WidePtr in favor of PtrConst<'a>/PtrMut<'a>/PtrUninit<'a>
- Add OxRef<'a>/OxMut<'a> - shaped pointers bundling ptr + shape
- Add Ox<'a> enum for ownership tracking (like Cow for shaped pointers)

- VTableDirect: for concrete types, uses raw *const ()/*mut (), returns T directly
- VTableIndirect: for generic containers, uses OxRef/OxMut, returns Option<T>
- VTableErased enum wraps both styles
- Shape::call_* helpers dispatch to either style transparently

- vtable_direct! macro for sized types with compile-time known traits
- VTableIndirect::builder() for generic containers with runtime dispatch
- All container vtables (Array, Option, Result, List, Map, Set) use builders
- No more positional arguments - named builder methods only

- Hash trait is generic over H: Hasher, can't store directly in vtable
- HashProxy type-erases the hasher for vtable storage
- Enables hash support without wrapper function bloat

- impls_core/impls_alloc/impls_std -> impls/core/alloc/std/crates
- New internal/ module for facet's own types (Shape, Def, etc.)
- Cleaner separation of concerns

- Copy, Send, Sync, Eq, Unpin stored as bitflags on Shape
- Set via ShapeBuilder methods: .copy(), .send(), .sync(), .eq()

- ThinPtr -> *const () or PtrConst<'a>
- WidePtr -> OxRef<'a> or OxMut<'a>
- vtable_ref! -> vtable_direct! or VTableIndirect::builder()
- ValueVTable -> VTableDirect or VTableIndirect
- Closures in vtables -> named fn items

WIP: Some impl modules still need migration to new API.

4092 of 8007 new or added lines in 112 files covered. (51.11%)

117 existing lines in 26 files now uncovered.

26173 of 44601 relevant lines covered (58.68%)

635.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.41
/facet-core/src/impls/core/pointer.rs
1
//! Facet implementation for raw pointers (*const T, *mut T)
2

3
use core::cmp::Ordering;
4
use core::hash::Hash;
5

6
use crate::{
7
    Def, Facet, HashProxy, OxPtrConst, OxPtrMut, PointerType, Shape, ShapeBuilder, Type,
8
    TypeOpsIndirect, TypeParam, VTableIndirect, ValuePointerType, Variance,
9
};
10

11
// For raw pointers, we use indirect vtable since they're generic over T
12
// However, they're scalars so the implementations are simple
13

14
/// Debug for *const T
15
unsafe fn const_ptr_debug<T: ?Sized>(
59✔
16
    ox: OxPtrConst,
59✔
17
    f: &mut core::fmt::Formatter<'_>,
59✔
18
) -> Option<core::fmt::Result> {
59✔
19
    let p = unsafe { ox.ptr().get::<*const T>() };
59✔
20
    Some(core::fmt::Debug::fmt(&p, f))
59✔
21
}
59✔
22

23
/// Hash for *const T
NEW
24
unsafe fn const_ptr_hash<T: ?Sized>(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
×
NEW
25
    let p = unsafe { ox.ptr().get::<*const T>() };
×
NEW
26
    p.hash(hasher);
×
NEW
27
    Some(())
×
NEW
28
}
×
29

30
/// PartialEq for *const T
31
#[allow(ambiguous_wide_pointer_comparisons)]
32
unsafe fn const_ptr_partial_eq<T: ?Sized>(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
12✔
33
    let a_val = unsafe { a.ptr().get::<*const T>() };
12✔
34
    let b_val = unsafe { b.ptr().get::<*const T>() };
12✔
35
    Some(*a_val == *b_val)
12✔
36
}
12✔
37

38
/// PartialOrd for *const T
39
#[allow(ambiguous_wide_pointer_comparisons)]
40
unsafe fn const_ptr_partial_cmp<T: ?Sized>(
12✔
41
    a: OxPtrConst,
12✔
42
    b: OxPtrConst,
12✔
43
) -> Option<Option<Ordering>> {
12✔
44
    let a_val = unsafe { a.ptr().get::<*const T>() };
12✔
45
    let b_val = unsafe { b.ptr().get::<*const T>() };
12✔
46
    Some(a_val.partial_cmp(b_val))
12✔
47
}
12✔
48

49
/// Ord for *const T
50
#[allow(ambiguous_wide_pointer_comparisons)]
51
unsafe fn const_ptr_cmp<T: ?Sized>(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
12✔
52
    let a_val = unsafe { a.ptr().get::<*const T>() };
12✔
53
    let b_val = unsafe { b.ptr().get::<*const T>() };
12✔
54
    Some(a_val.cmp(b_val))
12✔
55
}
12✔
56

57
/// Drop for *const T (no-op, pointers don't need dropping)
58
unsafe fn const_ptr_drop<T: ?Sized>(_ptr: OxPtrMut) {
8✔
59
    // Pointers don't need dropping
60
}
8✔
61

62
/// Clone for *const T (Copy types can be cloned by copying)
63
unsafe fn const_ptr_clone<T: ?Sized>(src: OxPtrConst, dst: OxPtrMut) {
8✔
64
    let src_val = unsafe { src.ptr().get::<*const T>() };
8✔
65
    let dst_ptr = unsafe { dst.ptr().as_mut::<*const T>() };
8✔
66
    *dst_ptr = *src_val;
8✔
67
}
8✔
68

69
/// Debug for *mut T
70
unsafe fn mut_ptr_debug<T: ?Sized>(
40✔
71
    ox: OxPtrConst,
40✔
72
    f: &mut core::fmt::Formatter<'_>,
40✔
73
) -> Option<core::fmt::Result> {
40✔
74
    let p = unsafe { ox.ptr().get::<*mut T>() };
40✔
75
    Some(core::fmt::Debug::fmt(&p, f))
40✔
76
}
40✔
77

78
/// Hash for *mut T
NEW
79
unsafe fn mut_ptr_hash<T: ?Sized>(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
×
NEW
80
    let p = unsafe { ox.ptr().get::<*mut T>() };
×
NEW
81
    p.hash(hasher);
×
NEW
82
    Some(())
×
NEW
83
}
×
84

85
/// PartialEq for *mut T
86
#[allow(ambiguous_wide_pointer_comparisons)]
87
unsafe fn mut_ptr_partial_eq<T: ?Sized>(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
8✔
88
    let a_val = unsafe { a.ptr().get::<*mut T>() };
8✔
89
    let b_val = unsafe { b.ptr().get::<*mut T>() };
8✔
90
    Some(*a_val == *b_val)
8✔
91
}
8✔
92

93
/// PartialOrd for *mut T
94
#[allow(ambiguous_wide_pointer_comparisons)]
95
unsafe fn mut_ptr_partial_cmp<T: ?Sized>(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
8✔
96
    let a_val = unsafe { a.ptr().get::<*mut T>() };
8✔
97
    let b_val = unsafe { b.ptr().get::<*mut T>() };
8✔
98
    Some(a_val.partial_cmp(b_val))
8✔
99
}
8✔
100

101
/// Ord for *mut T
102
#[allow(ambiguous_wide_pointer_comparisons)]
103
unsafe fn mut_ptr_cmp<T: ?Sized>(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
8✔
104
    let a_val = unsafe { a.ptr().get::<*mut T>() };
8✔
105
    let b_val = unsafe { b.ptr().get::<*mut T>() };
8✔
106
    Some(a_val.cmp(b_val))
8✔
107
}
8✔
108

109
/// Drop for *mut T (no-op, pointers don't need dropping)
110
unsafe fn mut_ptr_drop<T: ?Sized>(_ptr: OxPtrMut) {
8✔
111
    // Pointers don't need dropping
112
}
8✔
113

114
/// Clone for *mut T (Copy types can be cloned by copying)
115
unsafe fn mut_ptr_clone<T: ?Sized>(src: OxPtrConst, dst: OxPtrMut) {
8✔
116
    let src_val = unsafe { src.ptr().get::<*mut T>() };
8✔
117
    let dst_ptr = unsafe { dst.ptr().as_mut::<*mut T>() };
8✔
118
    *dst_ptr = *src_val;
8✔
119
}
8✔
120

121
// *const pointers
122
unsafe impl<'a, T: Facet<'a> + ?Sized> Facet<'a> for *const T {
123
    const SHAPE: &'static Shape = &const {
NEW
124
        const fn build_const_ptr_vtable<'a, T: Facet<'a> + ?Sized>() -> VTableIndirect {
×
NEW
125
            VTableIndirect {
×
NEW
126
                display: None,
×
NEW
127
                debug: Some(const_ptr_debug::<T>),
×
NEW
128
                hash: Some(const_ptr_hash::<T>),
×
NEW
129
                invariants: None,
×
NEW
130
                parse: None,
×
NEW
131
                try_from: None,
×
NEW
132
                try_into_inner: None,
×
NEW
133
                try_borrow_inner: None,
×
NEW
134
                partial_eq: Some(const_ptr_partial_eq::<T>),
×
NEW
135
                partial_cmp: Some(const_ptr_partial_cmp::<T>),
×
NEW
136
                cmp: Some(const_ptr_cmp::<T>),
×
NEW
137
            }
×
NEW
138
        }
×
139

NEW
140
        const fn build_const_ptr_type_ops<T: ?Sized>() -> TypeOpsIndirect {
×
NEW
141
            TypeOpsIndirect {
×
NEW
142
                drop_in_place: const_ptr_drop::<T>,
×
NEW
143
                default_in_place: None,
×
NEW
144
                clone_into: Some(const_ptr_clone::<T>),
×
NEW
145
            }
×
NEW
146
        }
×
147

148
        ShapeBuilder::for_sized::<*const T>("*const T")
149
            .ty({
150
                let is_wide = ::core::mem::size_of::<Self>() != ::core::mem::size_of::<*const ()>();
151
                let vpt = ValuePointerType {
152
                    mutable: false,
153
                    wide: is_wide,
154
                    target: T::SHAPE,
155
                };
156
                Type::Pointer(PointerType::Raw(vpt))
157
            })
158
            .def(Def::Scalar)
159
            .type_params(&[TypeParam {
160
                name: "T",
161
                shape: T::SHAPE,
162
            }])
163
            .inner(T::SHAPE)
164
            .vtable_indirect(&const { build_const_ptr_vtable::<T>() })
165
            .type_ops_indirect(&const { build_const_ptr_type_ops::<T>() })
166
            .eq()
167
            .copy()
168
            .build()
169
    };
170
}
171

172
// *mut pointers
173
unsafe impl<'a, T: Facet<'a> + ?Sized> Facet<'a> for *mut T {
174
    const SHAPE: &'static Shape = &const {
NEW
175
        const fn build_mut_ptr_vtable<'a, T: Facet<'a> + ?Sized>() -> VTableIndirect {
×
NEW
176
            VTableIndirect {
×
NEW
177
                display: None,
×
NEW
178
                debug: Some(mut_ptr_debug::<T>),
×
NEW
179
                hash: Some(mut_ptr_hash::<T>),
×
NEW
180
                invariants: None,
×
NEW
181
                parse: None,
×
NEW
182
                try_from: None,
×
NEW
183
                try_into_inner: None,
×
NEW
184
                try_borrow_inner: None,
×
NEW
185
                partial_eq: Some(mut_ptr_partial_eq::<T>),
×
NEW
186
                partial_cmp: Some(mut_ptr_partial_cmp::<T>),
×
NEW
187
                cmp: Some(mut_ptr_cmp::<T>),
×
NEW
188
            }
×
NEW
189
        }
×
190

NEW
191
        const fn build_mut_ptr_type_ops<T: ?Sized>() -> TypeOpsIndirect {
×
NEW
192
            TypeOpsIndirect {
×
NEW
193
                drop_in_place: mut_ptr_drop::<T>,
×
NEW
194
                default_in_place: None,
×
NEW
195
                clone_into: Some(mut_ptr_clone::<T>),
×
NEW
196
            }
×
NEW
197
        }
×
198

199
        ShapeBuilder::for_sized::<*mut T>("*mut T")
200
            .ty({
201
                let is_wide = ::core::mem::size_of::<Self>() != ::core::mem::size_of::<*const ()>();
202
                let vpt = ValuePointerType {
203
                    mutable: true,
204
                    wide: is_wide,
205
                    target: T::SHAPE,
206
                };
207
                Type::Pointer(PointerType::Raw(vpt))
208
            })
209
            .def(Def::Scalar)
210
            .type_params(&[TypeParam {
211
                name: "T",
212
                shape: T::SHAPE,
213
            }])
214
            .inner(T::SHAPE)
215
            .vtable_indirect(&const { build_mut_ptr_vtable::<T>() })
216
            .type_ops_indirect(&const { build_mut_ptr_type_ops::<T>() })
217
            // *mut T is invariant in T
218
            .variance(Variance::INVARIANT)
219
            .eq()
220
            .copy()
221
            .build()
222
    };
223
}
224

225
#[cfg(test)]
226
mod test {
227
    use core::panic::{RefUnwindSafe, UnwindSafe};
228

229
    #[cfg(feature = "auto-traits")]
230
    use impls::impls;
231

232
    #[allow(unused)]
NEW
233
    const fn assert_impls_unwind_safe<T: UnwindSafe>() {}
×
234
    #[allow(unused)]
NEW
235
    const fn assert_impls_ref_unwind_safe<T: RefUnwindSafe>() {}
×
236

237
    #[allow(unused)]
NEW
238
    const fn ref_unwind_safe<T: RefUnwindSafe>() {
×
NEW
239
        assert_impls_unwind_safe::<&T>();
×
NEW
240
        assert_impls_ref_unwind_safe::<&T>();
×
241

NEW
242
        assert_impls_ref_unwind_safe::<&mut T>();
×
243

NEW
244
        assert_impls_unwind_safe::<*const T>();
×
NEW
245
        assert_impls_ref_unwind_safe::<*const T>();
×
246

NEW
247
        assert_impls_unwind_safe::<*mut T>();
×
NEW
248
        assert_impls_ref_unwind_safe::<*mut T>();
×
NEW
249
    }
×
250

251
    #[test]
252
    #[cfg(feature = "auto-traits")]
253
    fn mut_ref_not_unwind_safe() {
254
        assert!(impls!(&mut (): !UnwindSafe));
255
    }
256
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc