Skip to main content

qt_core/
q_ptr.rs

1use crate::{QBox, QObject, QPointerOfQObject};
2use cpp_core::{
3    CastFrom, CastInto, CppBox, CppDeletable, DynamicCast, Ptr, Ref, StaticDowncast, StaticUpcast,
4};
5use std::fmt;
6use std::ops::Deref;
7
8/// A smart pointer that automatically sets to null when the object is deleted.
9///
10/// `QPtr` exposes functionality provided by the `QPointer<T>` C++ class.
11/// `QPtr` can only contain a pointer to a `QObject`-based object. When that object is
12/// deleted, `QPtr` automatically becomes a null pointer.
13///
14/// Note that dereferencing a null `QPtr` will panic, so if it's known that the object may
15/// already have been deleted, you should use `is_null()`, `as_ref()`,
16/// or a similar method to check
17/// if the object is still alive before calling its methods.
18///
19/// `QPtr` is not an owning pointer, similar to `cpp_core::Ptr`. If you actually own the object,
20/// you should convert it to `QBox` (it will delete the object when dropped if it has no parent)
21/// or `CppBox` (it will always delete the object when dropped). `QPtr` provides `into_qbox` and
22/// `to_box` helpers for that.
23///
24/// # Safety
25///
26/// While `QPtr` is much safer than `cpp_core::Ptr` and prevents use-after-free in common cases,
27/// it is unsafe to use in Rust terms. `QPtr::new` must receive a valid pointer or a null pointer,
28/// otherwise the behavior is undefined. You should not store pointers of other types
29/// (e.g. `Ptr`, `Ref`, or raw pointers) produced by `QPtr` because, unlike `QPtr`, these
30/// pointers will not become null pointers when the object is deleted.
31///
32/// It's still possible to cause use-after-free by calling a method through `QPtr`.
33/// Even in a single threaded program, the accessed object can be deleted by a nested call
34/// while one of its methods is still running. In multithreaded context, the object can be deleted
35/// in another thread between the null check and the method call, also resulting in undefined
36/// behavior.
37pub struct QPtr<T: StaticUpcast<QObject>> {
38    q_pointer: Option<CppBox<QPointerOfQObject>>,
39    target: Ptr<T>,
40}
41
42impl<T: StaticUpcast<QObject>> QPtr<T> {
43    /// Creates a `QPtr` from a `Ptr`.
44    ///
45    /// ### Safety
46    ///
47    /// `target` must be either a valid pointer to an object or a null pointer.
48    /// See type level documentation.
49    pub unsafe fn new(target: impl CastInto<Ptr<T>>) -> Self {
50        let target = target.cast_into();
51        QPtr {
52            q_pointer: if target.is_null() {
53                None
54            } else {
55                Some(QPointerOfQObject::new_1a(Ptr::from_raw(
56                    target.as_raw_ptr(),
57                )))
58            },
59            target,
60        }
61    }
62
63    /// Creates a `QPtr` from a raw pointer.
64    ///
65    /// ### Safety
66    ///
67    /// `target` must be either a valid pointer to an object or a null pointer.
68    /// See type level documentation.
69    pub unsafe fn from_raw(target: *const T) -> Self {
70        Self::new(Ptr::from_raw(target))
71    }
72
73    /// Creates a null pointer.
74    ///
75    /// Note that you can also use `NullPtr` to specify a null pointer to a function accepting
76    /// `impl CastInto<Ptr<_>>`. Unlike `Ptr`, `NullPtr` is not a generic type, so it will
77    /// not cause type inference issues.
78    ///
79    /// Note that accessing the content of a null `QPtr` through `Deref` will result
80    /// in a panic.
81    ///
82    /// ### Safety
83    ///
84    /// Null pointers must not be dereferenced. See type level documentation.
85    pub unsafe fn null() -> Self {
86        Self::new(Ptr::<T>::null())
87    }
88
89    /// Returns true if the pointer is null.
90    ///
91    /// ### Safety
92    ///
93    /// See type level documentation.
94    pub unsafe fn is_null(&self) -> bool {
95        self.q_pointer.as_ref().map_or(true, |p| p.is_null())
96    }
97
98    /// Returns the content as a const `Ptr`.
99    ///
100    /// ### Safety
101    ///
102    /// See type level documentation.
103    pub unsafe fn as_ptr(&self) -> Ptr<T> {
104        if self.is_null() {
105            Ptr::null()
106        } else {
107            self.target
108        }
109    }
110
111    /// Returns the content as a raw const pointer.
112    ///
113    /// ### Safety
114    ///
115    /// See type level documentation.
116    pub unsafe fn as_raw_ptr(&self) -> *const T {
117        self.as_ptr().as_raw_ptr()
118    }
119
120    /// Returns the content as a raw pointer.
121    ///
122    /// ### Safety
123    ///
124    /// See type level documentation.
125    pub unsafe fn as_mut_raw_ptr(&self) -> *mut T {
126        self.as_ptr().as_mut_raw_ptr()
127    }
128
129    /// Returns the content as a const `Ref`. Returns `None` if `self` is a null pointer.
130    ///
131    /// ### Safety
132    ///
133    /// See type level documentation.
134    pub unsafe fn as_ref(&self) -> Option<Ref<T>> {
135        self.as_ptr().as_ref()
136    }
137
138    /// Returns a reference to the value. Returns `None` if the pointer is null.
139    ///
140    /// ### Safety
141    ///
142    /// `self` must be valid.
143    /// The content must not be read or modified through other ways while the returned reference
144    /// exists.See type level documentation.
145    pub unsafe fn as_raw_ref<'a>(&self) -> Option<&'a T> {
146        self.as_ref().map(|r| r.as_raw_ref())
147    }
148
149    /// Returns a mutable reference to the value. Returns `None` if the pointer is null.
150    ///
151    /// ### Safety
152    ///
153    /// `self` must be valid.
154    /// The content must not be read or modified through other ways while the returned reference
155    /// exists.See type level documentation.
156    pub unsafe fn as_mut_raw_ref<'a>(&self) -> Option<&'a mut T> {
157        self.as_ref().map(|r| r.as_mut_raw_ref())
158    }
159
160    /// Converts the pointer to the base class type `U`.
161    ///
162    /// ### Safety
163    ///
164    /// This operation is safe as long as `self` is valid or null. See type level documentation.
165    pub unsafe fn static_upcast<U>(&self) -> QPtr<U>
166    where
167        T: StaticUpcast<U>,
168        U: StaticUpcast<QObject>,
169    {
170        QPtr::<U>::new(self.as_ptr().static_upcast::<U>())
171    }
172
173    /// Converts the pointer to the derived class type `U`.
174    ///
175    /// It's recommended to use `dynamic_cast` instead because it performs a checked conversion.
176    ///
177    /// ### Safety
178    ///
179    /// This operation is safe as long as `self` is valid and it's type is `U` or inherits from `U`,
180    /// of if `self` is a null pointer. See type level documentation.
181    pub unsafe fn static_downcast<U>(&self) -> QPtr<U>
182    where
183        T: StaticDowncast<U>,
184        U: StaticUpcast<QObject>,
185    {
186        QPtr::<U>::new(self.as_ptr().static_downcast())
187    }
188
189    /// Converts the pointer to the derived class type `U`. Returns `None` if the object's type
190    /// is not `U` and doesn't inherit `U`.
191    ///
192    /// ### Safety
193    ///
194    /// This operation is safe as long as `self` is valid or null. See type level documentation.
195    pub unsafe fn dynamic_cast<U>(&self) -> QPtr<U>
196    where
197        T: DynamicCast<U>,
198        U: StaticUpcast<QObject>,
199    {
200        QPtr::<U>::new(self.as_ptr().dynamic_cast())
201    }
202
203    /// Converts this pointer to a `CppBox`. Returns `None` if `self`
204    /// is a null pointer.
205    ///
206    /// Use this function to take ownership of the object. This is
207    /// the same as `CppBox::new`. `CppBox` will delete the object when dropped.
208    ///
209    /// You can also use `into_qbox` to convert the pointer to a `QBox`.
210    /// Unlike `CppBox`, `QBox` will only delete the object if it has no parent.
211    ///
212    /// ### Safety
213    ///
214    /// `CppBox` will attempt to delete the object on drop. If something else also tries to
215    /// delete this object before or after that, the behavior is undefined.
216    /// See type level documentation.
217    pub unsafe fn to_box(&self) -> Option<CppBox<T>>
218    where
219        T: CppDeletable,
220    {
221        self.as_ptr().to_box()
222    }
223
224    /// Converts this pointer to a `QBox`.
225    ///
226    /// Use this function to take ownership of the object. This is
227    /// the same as `QBox::from_q_mut_ptr`.
228    ///
229    /// ### Safety
230    ///
231    /// See type level documentation.
232    pub unsafe fn into_q_box(self) -> QBox<T>
233    where
234        T: CppDeletable,
235    {
236        QBox::from_q_ptr(self)
237    }
238}
239
240/// Creates another pointer to the same object.
241impl<T: StaticUpcast<QObject>> Clone for QPtr<T> {
242    fn clone(&self) -> Self {
243        unsafe { QPtr::<T>::new(self.as_ptr()) }
244    }
245}
246
247impl<T: StaticUpcast<QObject>> fmt::Debug for QPtr<T> {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        write!(f, "QPtr({:?})", unsafe { self.as_raw_ptr() })
250    }
251}
252
253/// Allows to call member functions of `T` and its base classes directly on the pointer.
254///
255/// Panics if the pointer is null.
256impl<T: StaticUpcast<QObject>> Deref for QPtr<T> {
257    type Target = T;
258
259    fn deref(&self) -> &T {
260        unsafe {
261            let ptr = self.as_raw_ptr();
262            if ptr.is_null() {
263                panic!("attempted to deref a null QPtr<T>");
264            }
265            &*ptr
266        }
267    }
268}
269
270impl<'a, T, U> CastFrom<&'a QPtr<U>> for Ptr<T>
271where
272    U: StaticUpcast<T> + StaticUpcast<QObject>,
273{
274    unsafe fn cast_from(value: &'a QPtr<U>) -> Self {
275        CastFrom::cast_from(value.as_ptr())
276    }
277}
278
279impl<T, U> CastFrom<QPtr<U>> for Ptr<T>
280where
281    U: StaticUpcast<T> + StaticUpcast<QObject>,
282{
283    unsafe fn cast_from(value: QPtr<U>) -> Self {
284        CastFrom::cast_from(value.as_ptr())
285    }
286}