Struct OnReady
pub struct OnReady<T> { /* private fields */ }
Expand description
Ergonomic late-initialization container with ready()
support.
While deferred initialization is generally seen as bad practice, it is often inevitable in game development.
Godot in particular encourages initialization inside ready()
, e.g. to access the scene tree after a node is inserted into it.
The alternative to using this pattern is Option<T>
, which needs to be explicitly unwrapped with unwrap()
or expect()
each time.
If you have a value that you expect to be initialized in the Godot editor, use OnEditor<T>
instead.
As a general “maybe initialized” type, Option<Gd<T>>
is always available, even if more verbose.
§Late-init semantics
OnReady<T>
should always be used as a struct field. There are two modes to use it:
- Automatic mode, using
new()
,from_base_fn()
,from_node()
orfrom_loaded()
.
Beforeready()
is called, allOnReady
fields constructed with the above methods are automatically initialized, in the order of declaration. This means that you can safely access them inready()
. - Manual mode, using
manual()
.
These fields are left uninitialized until you callinit()
on them. This is useful if you need more complex initialization scenarios than a closure allows. If you forget initialization, a panic will occur on first access.
Conceptually, OnReady<T>
is very close to once_cell’s Lazy<T>
, with additional hooks into the Godot lifecycle.
The absence of methods to check initialization state is deliberate: you don’t need them if you follow the above two patterns.
This container is not designed as a general late-initialization solution, but tailored to the ready()
semantics of Godot.
OnReady<T>
cannot be used with #[export]
fields, because ready()
is typically not called in the editor (unless #[class(tool)]
is specified). You can however use it with #[var]
– just make sure to access the fields in GDScript after ready()
.
This type is not thread-safe. ready()
runs on the main thread, and you are expected to access its value on the main thread, as well.
§Requirements
- The class must have an explicit
Base
field (i.e.base: Base<Node>
). - The class must inherit
Node
(otherwiseready()
would not exist anyway).
§Example - user-defined init
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base = Node)]
struct MyClass {
base: Base<Node>,
auto: OnReady<i32>,
manual: OnReady<i32>,
}
#[godot_api]
impl INode for MyClass {
fn init(base: Base<Node>) -> Self {
Self {
base,
auto: OnReady::new(|| 11),
manual: OnReady::manual(),
}
}
fn ready(&mut self) {
// self.auto is now ready with value 11.
assert_eq!(*self.auto, 11);
// self.manual needs to be initialized manually.
self.manual.init(22);
assert_eq!(*self.manual, 22);
}
}
§Example - macro-generated init
use godot::prelude::*;
#[derive(GodotClass)]
#[class(init, base = Node)]
struct MyClass {
base: Base<Node>,
#[init(node = "ChildPath")]
auto: OnReady<Gd<Node2D>>,
#[init(val = OnReady::manual())]
manual: OnReady<i32>,
}
#[godot_api]
impl INode for MyClass {
fn ready(&mut self) {
// self.node is now ready with the node found at path `ChildPath`.
assert_eq!(self.auto.get_name(), "ChildPath".into());
// self.manual needs to be initialized manually.
self.manual.init(22);
assert_eq!(*self.manual, 22);
}
}
Implementations§
§impl<T> OnReady<Gd<T>>
impl<T> OnReady<Gd<T>>
pub fn from_node(path: impl AsArg<NodePath>) -> OnReady<Gd<T>>
pub fn from_node(path: impl AsArg<NodePath>) -> OnReady<Gd<T>>
Variant of OnReady::new()
, fetching the node located at path
before ready()
.
This is the functional equivalent of:
- the GDScript pattern
@onready var node = $NODE_PATH
. - the Rust method
Node::get_node_as()
.
When used with #[class(init)]
, the field can be annotated with #[init(node = "NODE_PATH")]
to call this constructor.
§Panics (deferred)
- If
path
does not point to a valid node, or its type is not aT
or a subclass.
Note that the panic will only happen if and when the node enters the SceneTree for the first time
(i.e. it receives the READY
notification).
pub fn node(path: impl AsArg<NodePath>) -> OnReady<Gd<T>>
from_node
.§impl<T> OnReady<Gd<T>>
impl<T> OnReady<Gd<T>>
pub fn from_loaded(path: impl AsArg<GString>) -> OnReady<Gd<T>>
pub fn from_loaded(path: impl AsArg<GString>) -> OnReady<Gd<T>>
Variant of OnReady::new()
, loading the resource stored at path
before ready()
.
This is the functional equivalent of:
- the GDScript pattern
@onready var res = load(...)
. - the Rust function
tools::load()
.
When used with #[class(init)]
, the field can be annotated with #[init(load = "FILE_PATH")]
to call this constructor.
§Panics (deferred)
- If the resource does not exist at
path
, cannot be loaded or is not compatible with typeT
.
Note that the panic will only happen if and when the node enters the SceneTree for the first time
(i.e. it receives the READY
notification).
§impl<T> OnReady<T>
impl<T> OnReady<T>
pub fn new<F>(init_fn: F) -> OnReady<T>where
F: FnOnce() -> T + 'static,
pub fn new<F>(init_fn: F) -> OnReady<T>where
F: FnOnce() -> T + 'static,
Schedule automatic initialization before ready()
.
This guarantees that the value is initialized once ready()
starts running.
Until then, accessing the object may panic. In particular, the object is not initialized on first use.
The value is also initialized when you don’t override ready()
.
For more control over initialization, use the OnReady::manual()
constructor, followed by a self.init()
call during ready()
.
pub fn from_base_fn<F>(init_fn: F) -> OnReady<T>
pub fn from_base_fn<F>(init_fn: F) -> OnReady<T>
Variant of OnReady::new()
, allowing access to Base
when initializing.
pub fn manual() -> OnReady<T>
pub fn manual() -> OnReady<T>
Leave uninitialized, expects manual initialization during ready()
.
If you use this method, you must call init()
during the ready()
callback, otherwise a panic will occur.
pub fn init(&mut self, value: T)
pub fn init(&mut self, value: T)
Runs manual initialization.
§Panics
- If
init()
was called before. - If this object was already provided with a closure during construction, in
Self::new()
or any other automatic constructor.
Trait Implementations§
§impl<T> GodotConvert for OnReady<T>where
T: GodotConvert,
impl<T> GodotConvert for OnReady<T>where
T: GodotConvert,
§type Via = <T as GodotConvert>::Via
type Via = <T as GodotConvert>::Via
Self
is represented in Godot.§impl<T> Var for OnReady<T>where
T: Var,
impl<T> Var for OnReady<T>where
T: Var,
fn get_property(&self) -> <OnReady<T> as GodotConvert>::Via
fn set_property(&mut self, value: <OnReady<T> as GodotConvert>::Via)
§fn var_hint() -> PropertyHintInfo
fn var_hint() -> PropertyHintInfo
GodotType::property_info
, e.g. for enums/newtypes.