Skip to content

Commit 401545c

Browse files
committed
Clarify Export semantics for objects
1 parent 47cc8da commit 401545c

File tree

3 files changed

+42
-20
lines changed

3 files changed

+42
-20
lines changed

godot-core/src/obj/dyn_gd.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use std::{fmt, ops};
4646
/// #[derive(GodotClass)]
4747
/// #[class(init)]
4848
/// struct Monster {
49-
/// #[init(val = 100)]
49+
/// #[init(val = 100)]
5050
/// hitpoints: u16,
5151
/// }
5252
///
@@ -137,24 +137,39 @@ use std::{fmt, ops};
137137
/// and it can query the dynamic type of the object. Based on that type, it can find the `impl Health` implementation matching the correct class.
138138
/// Behind the scenes, everything is wired up correctly so that you can restore the original `DynGd` even after it has passed through Godot.
139139
///
140-
/// # `#[export]` for `DynGd<T, D>`
140+
/// # Exporting
141141
///
142-
/// Exporting `DynGd<T, D>` is possible only via [`OnEditor`] or [`Option`].
142+
/// [Like `Gd<T>`](struct.Gd.html#exporting), using `#[export]` with `DynGd<T, D>` is possible only via [`OnEditor`] or [`Option`].
143143
/// `DynGd<T, D>` can also be exported directly as an element of an array such as `Array<DynGd<T, D>>`.
144144
///
145-
/// Since `DynGd<T, D>` represents shared functionality `D` across classes inheriting from `T`,
146-
/// consider using `#[export] Gd<T>` instead of `#[export] DynGd<T, D>`
147-
/// in cases when `T` is a concrete Rust `GodotClass`.
145+
/// When talking about "exporting", the following paragraphs assume that you wrap `DynGd` in one of those types.
148146
///
149-
/// ## Node based classes
147+
/// In cases where `T: AsDyn<D>` (the trait is directly implemented on the user class, i.e. no upcasting), exporting `DynGd<T, D>` is
148+
/// equivalent to exporting `Gd<T>` regarding Inspector UI.
150149
///
151-
/// `#[export]` for a `DynGd<T, D>` works identically to `#[export]` `Gd<T>` for `T` inheriting Node classes.
152-
/// Godot will report an error if the conversion fails, but it will only do so when accessing the given value.
150+
/// ## Node-based classes
153151
///
154-
/// ## Resource based classes
152+
/// If `T` inherits `Node`, exporting `DynGd<T, D>` works identically to `Gd<T>`.
155153
///
156-
/// `#[export]` for a `DynGd<T, D>` allows you to limit the available choices to implementors of a given trait `D` whose base inherits the specified `T`
157-
/// (for example, `#[export] Option<DynGd<Resource, dyn MyTrait>>` won't include Rust classes with an Object base, even if they implement `MyTrait`).
154+
/// If you try to assign a class from the editor that does not implement trait `D`, Godot will report a conversion-failed error,
155+
/// but it will only do so when accessing the given value.
156+
///
157+
/// ## Resource-based classes
158+
///
159+
/// If `T` inherits `Resource`, exporting `DynGd<T, D>>` will limit the available choices to known implementors of the trait `D`.
160+
///
161+
/// For example, let's say you have four Rust classes:
162+
///
163+
/// | Class | Inherits | Implements trait |
164+
/// |--------------|------------|------------------|
165+
/// | `Bullet` | `Resource` | `Projectile` |
166+
/// | `Rocket` | `Resource` | `Projectile` |
167+
/// | `BulletNode` | `Node` | `Projectile` |
168+
/// | `Tower` | `Resource` | (none) |
169+
///
170+
/// Then, an exported `DynGd<Resource, dyn Projectile>` would be visible in Godot's Inspector UI with a drop-down field, i.e. users can assign
171+
/// only objects of certain classes. **The available options for the drop-down are `Bullet` and `Rocket`.** The class `BulletNode` is not
172+
/// available because it's not a `Resource`, and `Tower` is not because it doesn't implement the `Projectile` trait.
158173
///
159174
/// # Type inference
160175
///
@@ -581,9 +596,7 @@ where
581596
}
582597
}
583598

584-
/// `#[export]` for `Option<DynGd<T, D>>` is available only for `T` being Engine class (such as Node or Resource).
585-
///
586-
/// Consider exporting `Option<Gd<T>>` instead of `Option<DynGd<T, D>>` for user-declared GDExtension classes.
599+
/// See [`DynGd` Exporting](struct.DynGd.html#exporting) section.
587600
impl<T, D> Export for Option<DynGd<T, D>>
588601
where
589602
T: GodotClass + Bounds<Exportable = bounds::Yes>,
@@ -632,9 +645,7 @@ where
632645
}
633646
}
634647

635-
/// `#[export]` for `OnEditor<DynGd<T, D>>` is available only for `T` being Engine class (such as Node or Resource).
636-
///
637-
/// Consider exporting `OnEditor<Gd<T>>` instead of `OnEditor<DynGd<T, D>>` for user-declared GDExtension classes.
648+
/// See [`DynGd` Exporting](struct.DynGd.html#exporting) section.
638649
impl<T, D> Export for OnEditor<DynGd<T, D>>
639650
where
640651
Self: Var,

godot-core/src/obj/gd.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ use crate::{classes, out};
9090
///
9191
/// For type conversions, please read the [`godot::meta` module docs][crate::meta].
9292
///
93+
/// # Exporting
94+
///
95+
/// The [`Export`][crate::registry::property::Export] trait is not directly implemented for `Gd<T>`, because the editor expects object-based
96+
/// properties to be nullable, while `Gd<T>` can't be null. Instead, `Export` is implemented for [`OnEditor<Gd<T>>`][crate::obj::OnEditor],
97+
/// which validates that objects have been set by the editor. For the most flexible but least ergonomic option, you can also export
98+
/// `Option<Gd<T>>` fields.
99+
///
100+
/// Objects can only be exported if `T: Inherits<Node>` or `T: Inherits<Resource>`, just like GDScript.
101+
/// This means you cannot use `#[export]` with `OnEditor<Gd<RefCounted>>`, for example.
102+
///
93103
/// [book]: https://godot-rust.github.io/book/godot-api/objects.html
94104
/// [`Object`]: classes::Object
95105
/// [`RefCounted`]: classes::RefCounted
@@ -949,6 +959,7 @@ impl<T: GodotClass> Var for Gd<T> {
949959
}
950960
}
951961

962+
/// See [`Gd` Exporting](struct.Gd.html#exporting) section.
952963
impl<T> Export for Option<Gd<T>>
953964
where
954965
T: GodotClass + Bounds<Exportable = bounds::Yes>,
@@ -991,6 +1002,7 @@ where
9911002
}
9921003
}
9931004

1005+
/// See [`Gd` Exporting](struct.Gd.html#exporting) section.
9941006
impl<T> Export for OnEditor<Gd<T>>
9951007
where
9961008
Self: Var,

godot-core/src/registry/property.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ pub trait Var: GodotConvert {
5454
// Note: HTML link for #[export] works if this symbol is inside prelude, but not in register::property.
5555
/// Trait implemented for types that can be used as [`#[export]`](../register/derive.GodotClass.html#properties-and-exports) fields.
5656
///
57-
/// `Export` is only implemented for objects `Gd<T>` if either `T: Inherits<Node>` or `T: Inherits<Resource>`, just like GDScript.
58-
/// This means you cannot use `#[export]` with `Gd<RefCounted>`, for example.
57+
/// To export objects, see the [_Exporting_ section of `Gd<T>`](../obj/struct.Gd.html#exporting).
5958
///
6059
/// For enums, this trait can be derived using the [`#[derive(Export)]`](../derive.Export.html) macro.
6160
#[doc(alias = "property")]

0 commit comments

Comments
 (0)