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}