godot_core/builtin/rid.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use std::num::NonZeroU64;
9
10use godot_ffi as sys;
11use sys::{ffi_methods, static_assert, static_assert_eq_size_align, ExtVariantType, GodotFfi};
12
13/// A RID ("resource ID") is an opaque handle that refers to a Godot `Resource`.
14///
15/// RIDs do not grant access to the resource itself. Instead, they can be used in lower-level resource APIs
16/// such as the [servers]. See also [Godot API docs for `RID`][docs].
17///
18/// RIDs should be largely safe to work with. Certain calls to servers may fail, however doing so will
19/// trigger an error from Godot, and will not cause any UB.
20///
21/// # Safety caveat
22///
23/// In Godot 3, `RID` was not as safe as described here. We believe that this is fixed in Godot 4, but this has
24/// not been extensively tested as of yet. Some confirmed UB from Godot 3 does not occur anymore, but if you
25/// find anything suspicious or outright UB please open an issue.
26///
27/// [servers]: https://docs.godotengine.org/en/stable/tutorials/optimization/using_servers.html
28/// [docs]: https://docs.godotengine.org/en/stable/classes/class_rid.html
29///
30/// # Godot docs
31///
32/// [`RID` (stable)](https://docs.godotengine.org/en/stable/classes/class_rid.html)
33
34// Using normal Rust repr to take advantage of the nullable pointer optimization. As this enum is
35// eligible for it, it is also guaranteed to have it. Meaning the layout of this type is identical to `u64`.
36// See: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization
37// Cannot use `#[repr(C)]` as it does not use the nullable pointer optimization.
38#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
39pub enum Rid {
40 /// A valid RID may refer to some resource, but is not guaranteed to do so.
41 Valid(NonZeroU64),
42
43 /// An invalid RID will never refer to a resource. Internally it is represented as a 0.
44 Invalid,
45}
46
47// Ensure that `Rid`s actually have the layout we expect. Since `Rid` has the same size as `u64`, it cannot
48// have any padding. As the `Valid` variant must take up all but one of the niches (since it contains a
49// `NonZerou64`), and the `Invalid` variant must take up the final niche.
50static_assert_eq_size_align!(Rid, u64);
51
52// SAFETY:
53// As Rid and u64 have the same size, and `Rid::Invalid` is initialized, it must be represented by some `u64`
54// Therefore we can safely transmute it to u64.
55//
56// Ensure that `Rid::Invalid` actually is represented by 0, as it should be.
57static_assert!(unsafe { std::mem::transmute::<Rid, u64>(Rid::Invalid) } == 0u64);
58
59impl Rid {
60 /// Create a new RID.
61 #[inline]
62 pub const fn new(id: u64) -> Self {
63 match NonZeroU64::new(id) {
64 Some(id) => Self::Valid(id),
65 None => Self::Invalid,
66 }
67 }
68
69 /// Convert this RID into a [`u64`]. Returns 0 if it is invalid.
70 ///
71 /// _Godot equivalent: `Rid.get_id()`_
72 #[doc(alias = "get_id")]
73 #[inline]
74 pub const fn to_u64(self) -> u64 {
75 match self {
76 Rid::Valid(id) => id.get(),
77 Rid::Invalid => 0,
78 }
79 }
80
81 /// Convert this RID into a [`u64`] if it is valid. Otherwise, return None.
82 #[inline]
83 pub const fn to_valid_u64(self) -> Option<u64> {
84 match self {
85 Rid::Valid(id) => Some(id.get()),
86 Rid::Invalid => None,
87 }
88 }
89
90 /// Returns `true` if this is a valid RID.
91 #[inline]
92 pub const fn is_valid(&self) -> bool {
93 matches!(self, Rid::Valid(_))
94 }
95
96 /// Returns `true` if this is an invalid RID.
97 #[inline]
98 pub const fn is_invalid(&self) -> bool {
99 matches!(self, Rid::Invalid)
100 }
101}
102
103impl std::fmt::Display for Rid {
104 /// Formats `Rid` to match Godot's string representation.
105 ///
106 /// # Example
107 /// ```
108 /// use godot::prelude::*;
109 /// let id = Rid::new(1);
110 /// assert_eq!(format!("{}", id), "RID(1)");
111 /// ```
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 // godot output: `RID(0)`
114 match self {
115 Rid::Valid(x) => write!(f, "RID({x})"),
116 Rid::Invalid => write!(f, "RID(0)"),
117 }
118 }
119}
120
121// SAFETY:
122// This type is represented as `Self` in Godot, so `*mut Self` is sound.
123unsafe impl GodotFfi for Rid {
124 const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::RID);
125
126 ffi_methods! { type sys::GDExtensionTypePtr = *mut Self;
127 fn new_from_sys;
128 fn new_with_uninit;
129 fn from_arg_ptr;
130 fn sys;
131 fn sys_mut;
132 fn move_return_ptr;
133 }
134
135 unsafe fn new_with_init(init: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
136 let mut rid = Self::Invalid;
137 init(rid.sys_mut());
138 rid
139 }
140}
141
142crate::meta::impl_godot_as_self!(Rid: ByValue);