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.
OnReady<T>
should always be used as a field. There are two modes to use it:
- Automatic mode, using
new()
,from_base_fn()
ornode()
.
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 node(path: impl AsArg<NodePath>) -> OnReady<Gd<T>>
pub fn 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 = $NodePath
.
§Panics
- If
path
does not point to a valid node.
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()
.
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.