Skip to content

Commit 36390a8

Browse files
committed
Documented Handles and Assets (#3348)
# Objective Add documentation to [Handle](https://github.com/bevyengine/bevy/blob/7356f1586d74039f840bcfcf24af3e21c23e3c18/crates/bevy_asset/src/handle.rs#L63) and [Assets](https://github.com/bevyengine/bevy/blob/7356f1586d74039f840bcfcf24af3e21c23e3c18/crates/bevy_asset/src/assets.rs#L48). ## Tasks - [x] Document `Handle` - [x] Document `Assets` --- Fixes #3347 Co-authored-by: MrGVSV <[email protected]>
1 parent 24b21ea commit 36390a8

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

crates/bevy_asset/src/assets.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use crossbeam_channel::Sender;
99
use std::fmt::Debug;
1010

1111
/// Events that happen on assets of type `T`
12+
///
13+
/// Events sent via the [Assets] struct will always be sent with a _Weak_ handle
1214
pub enum AssetEvent<T: Asset> {
1315
Created { handle: Handle<T> },
1416
Modified { handle: Handle<T> },
@@ -44,6 +46,18 @@ impl<T: Asset> Debug for AssetEvent<T> {
4446
}
4547

4648
/// Stores Assets of a given type and tracks changes to them.
49+
///
50+
/// Each asset is mapped by a unique [HandleId](crate::HandleId), allowing any [Handle](crate::Handle)
51+
/// with the same HandleId to access it. These assets remain loaded for as long as a Strong handle
52+
/// to that asset exists.
53+
///
54+
/// To store a reference to an asset without forcing it to stay loaded, you can use a Weak handle.
55+
/// To make a Weak handle a Strong one, use [Assets::get_handle](crate::Assets::get_handle) or pass
56+
/// the Assets collection into the handle's [make_strong](crate::Handle::make_strong) method.
57+
///
58+
/// Remember, if there are no Strong handles for an asset (i.e. they have all been dropped), the asset
59+
/// will unload. Make sure you always have a Strong handle when you want to keep an asset loaded!
60+
///
4761
#[derive(Debug)]
4862
pub struct Assets<T: Asset> {
4963
assets: HashMap<HandleId, T>,
@@ -60,6 +74,10 @@ impl<T: Asset> Assets<T> {
6074
}
6175
}
6276

77+
/// Adds an asset to the collection, returning a Strong handle to that asset.
78+
///
79+
/// # Events
80+
/// * [`AssetEvent::Created`]
6381
pub fn add(&mut self, asset: T) -> Handle<T> {
6482
let id = HandleId::random::<T>();
6583
self.assets.insert(id, asset);
@@ -69,13 +87,27 @@ impl<T: Asset> Assets<T> {
6987
self.get_handle(id)
7088
}
7189

90+
/// Add/modify the asset pointed to by the given handle.
91+
///
92+
/// Unless there exists another Strong handle for this asset, it's advised to use the returned
93+
/// Strong handle. Not doing so may result in the unexpected release of the asset.
94+
///
95+
/// See [set_untracked](Assets::set_untracked) for more info.
7296
#[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
7397
pub fn set<H: Into<HandleId>>(&mut self, handle: H, asset: T) -> Handle<T> {
7498
let id: HandleId = handle.into();
7599
self.set_untracked(id, asset);
76100
self.get_handle(id)
77101
}
78102

103+
/// Add/modify the asset pointed to by the given handle.
104+
///
105+
/// If an asset already exists with the given HandleId, it will be modified. Otherwise the new asset
106+
/// will be inserted.
107+
///
108+
/// # Events
109+
/// * [`AssetEvent::Created`]: Sent if the asset did not yet exist with the given handle
110+
/// * [`AssetEvent::Modified`]: Sent if the asset with given handle already existed
79111
pub fn set_untracked<H: Into<HandleId>>(&mut self, handle: H, asset: T) {
80112
let id: HandleId = handle.into();
81113
if self.assets.insert(id, asset).is_some() {
@@ -89,14 +121,23 @@ impl<T: Asset> Assets<T> {
89121
}
90122
}
91123

124+
/// Get the asset for the given handle.
125+
///
126+
/// This is the main method for accessing asset data from an [Assets] collection. If you need
127+
/// mutable access to the asset, use [get_mut](Assets::get_mut).
92128
pub fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&T> {
93129
self.assets.get(&handle.into())
94130
}
95131

132+
/// Checks if an asset exists for the given handle
96133
pub fn contains<H: Into<HandleId>>(&self, handle: H) -> bool {
97134
self.assets.contains_key(&handle.into())
98135
}
99136

137+
/// Get mutable access to the asset for the given handle.
138+
///
139+
/// This is the main method for mutably accessing asset data from an [Assets] collection. If you
140+
/// do not need mutable access to the asset, you may also use [get](Assets::get).
100141
pub fn get_mut<H: Into<HandleId>>(&mut self, handle: H) -> Option<&mut T> {
101142
let id: HandleId = handle.into();
102143
self.events.send(AssetEvent::Modified {
@@ -105,10 +146,15 @@ impl<T: Asset> Assets<T> {
105146
self.assets.get_mut(&id)
106147
}
107148

149+
/// Gets a _Strong_ handle pointing to the same asset as the given one
108150
pub fn get_handle<H: Into<HandleId>>(&self, handle: H) -> Handle<T> {
109151
Handle::strong(handle.into(), self.ref_change_sender.clone())
110152
}
111153

154+
/// Get mutable access to an asset for the given handle, inserting a new value if none exists.
155+
///
156+
/// # Events
157+
/// * [`AssetEvent::Created`]: Sent if the asset did not yet exist with the given handle
112158
pub fn get_or_insert_with<H: Into<HandleId>>(
113159
&mut self,
114160
handle: H,
@@ -129,10 +175,12 @@ impl<T: Asset> Assets<T> {
129175
borrowed
130176
}
131177

178+
/// Get an iterator over all assets in the collection.
132179
pub fn iter(&self) -> impl Iterator<Item = (HandleId, &T)> {
133180
self.assets.iter().map(|(k, v)| (*k, v))
134181
}
135182

183+
/// Get a mutable iterator over all assets in the collection.
136184
pub fn iter_mut(&mut self) -> impl Iterator<Item = (HandleId, &mut T)> {
137185
for id in self.assets.keys() {
138186
self.events.send(AssetEvent::Modified {
@@ -142,10 +190,17 @@ impl<T: Asset> Assets<T> {
142190
self.assets.iter_mut().map(|(k, v)| (*k, v))
143191
}
144192

193+
/// Get an iterator over all HandleIds in the collection.
145194
pub fn ids(&self) -> impl Iterator<Item = HandleId> + '_ {
146195
self.assets.keys().cloned()
147196
}
148197

198+
/// Remove an asset for the given handle.
199+
///
200+
/// The asset is returned if it existed in the collection, otherwise `None`.
201+
///
202+
/// # Events
203+
/// * [`AssetEvent::Removed`]
149204
pub fn remove<H: Into<HandleId>>(&mut self, handle: H) -> Option<T> {
150205
let id: HandleId = handle.into();
151206
let asset = self.assets.remove(&id);
@@ -190,10 +245,12 @@ impl<T: Asset> Assets<T> {
190245
}
191246
}
192247

248+
/// Gets the number of assets in the collection
193249
pub fn len(&self) -> usize {
194250
self.assets.len()
195251
}
196252

253+
/// Returns true if there are no stored assets
197254
pub fn is_empty(&self) -> bool {
198255
self.assets.is_empty()
199256
}

crates/bevy_asset/src/handle.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,38 @@ impl HandleId {
6969
///
7070
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
7171
/// collection.
72+
///
73+
/// # Accessing the Asset
74+
///
75+
/// A handle is _not_ the asset itself, but should be seen as a pointer to the asset. Modifying a
76+
/// handle's `id` only modifies which asset is being pointed to. To get the actual asset, try using
77+
/// [`Assets::get`](crate::Assets::get) or [`Assets::get_mut`](crate::Assets::get_mut).
78+
///
79+
/// # Strong and Weak
80+
///
81+
/// A handle can be either "Strong" or "Weak". Simply put: Strong handles keep the asset loaded,
82+
/// while Weak handles do not affect the loaded status of assets. This is due to a type of
83+
/// _reference counting_. When the number of Strong handles that exist for any given asset reach
84+
/// zero, the asset is dropped and becomes unloaded. In some cases, you might want a reference to an
85+
/// asset but don't want to take the responsibility of keeping it loaded that comes with a Strong handle.
86+
/// This is where a Weak handle can be very useful.
87+
///
88+
/// For example, imagine you have a `Sprite` component and a `Collider` component. The `Collider` uses
89+
/// the `Sprite`'s image size to check for collisions. It does so by keeping a Weak copy of the
90+
/// `Sprite`'s Strong handle to the image asset.
91+
///
92+
/// If the `Sprite` is removed, its Strong handle to the image is dropped with it. And since it was the
93+
/// only Strong handle for that asset, the asset is unloaded. Our `Collider` component still has a Weak
94+
/// handle to the unloaded asset, but it will not be able to retrieve the image data, resulting in
95+
/// collisions no longer being detected for that entity.
96+
///
7297
#[derive(Component, Reflect, FromReflect)]
7398
#[reflect(Component)]
7499
pub struct Handle<T>
75100
where
76101
T: Asset,
77102
{
103+
/// The ID of the asset as contained within its respective [Assets](crate::Assets) collection
78104
pub id: HandleId,
79105
#[reflect(ignore)]
80106
handle_type: HandleType,
@@ -123,6 +149,7 @@ impl<T: Asset> Handle<T> {
123149
}
124150
}
125151

152+
/// Get a copy of this handle as a Weak handle
126153
pub fn as_weak<U: Asset>(&self) -> Handle<U> {
127154
Handle {
128155
id: self.id,
@@ -139,6 +166,9 @@ impl<T: Asset> Handle<T> {
139166
matches!(self.handle_type, HandleType::Strong(_))
140167
}
141168

169+
/// Makes this handle Strong if it wasn't already.
170+
///
171+
/// This method requires the corresponding [Assets](crate::Assets) collection
142172
pub fn make_strong(&mut self, assets: &mut Assets<T>) {
143173
if self.is_strong() {
144174
return;
@@ -266,6 +296,8 @@ impl<T: Asset> Clone for Handle<T> {
266296
///
267297
/// This allows handles to be mingled in a cross asset context. For example, storing `Handle<A>` and
268298
/// `Handle<B>` in the same `HashSet<HandleUntyped>`.
299+
///
300+
/// To convert back to a typed handle, use the [typed](HandleUntyped::typed) method.
269301
#[derive(Debug)]
270302
pub struct HandleUntyped {
271303
pub id: HandleId,
@@ -307,6 +339,9 @@ impl HandleUntyped {
307339
matches!(self.handle_type, HandleType::Strong(_))
308340
}
309341

342+
/// Convert this handle into a typed [Handle].
343+
///
344+
/// The new handle will maintain the Strong or Weak status of the current handle.
310345
pub fn typed<T: Asset>(mut self) -> Handle<T> {
311346
if let HandleId::Id(type_uuid, _) = self.id {
312347
if T::TYPE_UUID != type_uuid {

0 commit comments

Comments
 (0)