rust macros are magic

preview_player
Показать описание
Rust macros are magic.
I want to show you a real life application of a powerful rust macro I put together, that handles spell assets in the bevy game engine.

If you want to learn more about macro programming with Rust I recommend checking these videos out:

rust libraries used in this video:
bevy (game engine)
serde (serialization + deserialization)
ron (file format, similar to json. Ron integrates awesomely with rust)

Support me!
⁍ Monero: 43Ktj1Bd4Nkaj4fdx6nPvBZkJewcPjxPB9nafnepM7SdGtcU6rhpxyLiV9w3k92rE1UqHTr4BNqe2ScsK1eEENvZDC3W1ur

My discord group:

#gamedev #rustlang
Рекомендации по теме
Комментарии
Автор

you make this a proc macro (derive macro) you can create loader etc names from the struct and mark fields using attributes

atahanyorganci
Автор

I think this is a case where i would argue against this macro, since it makes the code way harder to understand. I think this could be done way nicer with a bit of derive macros and some generics and impls, which should make this code actually readable for someone new to the code. I know it might just be your personal project, so no one else has to lokk at it but if you have not used this part for weeks/months and then come back to it i promise you wont understand it at a glance/withou having to look through the code again to see what those magic ; and ? meant again.

Anyways, have a great day!

omgky
Автор

Quite interesting how Rust turns a high level language into assembly level legibility.

deltapi
Автор

This is extremely specific, but also exactly the info I needed! Are you a magician TanTan?

i_am_feenster
Автор

i think the macro is overkill, i think that traits and some generics could do this

okharev
Автор

Could you consider doing a bevy tutorial ? i would really like to see that tutorial series

godofpro
Автор

Let me just say, I have no complaints for using macros here. Seems like a good use case.

However, my first choice would have been using traits and generics. In fact, I wrote a bit of rust code that provides almost the same level of abstraction. You can have a generic RonAssetLoader<HeroAsset> and a RonAssetPlugin<HeroPlugin>, which is only marginally more typing effort than wehat the macros provide.

The only *real* challenge is to find a nice way of automatically loading nested assets. No matter what you do (both your solution and mine), having to specify again which field need reloading is annoying. Using derive macros is probably the only good option here (or maybe there's some serde magic).

Was fun wasting an hour or two on this :)

CheaterCodes
Автор

Easy to say you put a lot of effort on this video. High quality and pretty clarifying. Really appreciate sharing it.
Thanks!

codingleo
Автор

Beautiful. Just beautiful. Nicely done macro programming. Loving it.

DrIngo
Автор

I maybe coded in rust one hello world and wrote 1 game in Unity, so I'm not an expert in rust nor game programming, but seams to me that the last macro, the one you use in the tower defense game, is kinda doing too many things and it's unclear from outside (from the caller of the macro) what all those args do, especially the last 3 list an the "put some simbols where and there to differenciate".
Am I too much concerned about code readability?

mirkopassoli
Автор

If you don't want to write HeroAssetPlugin, HeroAssetLoader, HeroAsset, etc... by hand, you can make it simpler with paste crate. You can convert this macro that just takes an asset identifier and extensions identifier.

allxrise
Автор

5:38 I think it would make more sense to make asset_type from a real type just for some edge cases

lixou
Автор

Fascinating to see how these can be useful with Rust.

Skeffles
Автор

When the world needed him most, he uploaded.

LegoDinoMan
Автор

Please recreate this as a derive proc macro on the strict, really feels like this can be optimized and made even easier to read ergonomically

cchance
Автор

So coming from other languages i am a bit confused, couldnt the multiple versions of the same asset loader just for different file types simply be done using generics?

ariandannemann
Автор

I've ran into the exact same problem in my game where I have dozens of asset loaders. However i found that creating 'finalization' systems for assets is actually not necessary in most cases. For your Hero example, where you need to store a handle to another asset, you can actually make use of Bevy 'asset dependencies' to achieve this.

Inside of your Hero asset loader, you can utilize the path to the Spell asset and load a secondary asset using the AssetLoader 'load_context'.

Here is an example of what I do:

let mut hero: Hero =

let spell_bytes =
let spell: SpellAsset =

let spell_handle: Handle<SpellAsset> = load_context.set_labeled_asset("spell", LoadedAsset::new(spell));

hero.spell_handle = spell_handle;



This completely eliminates the need for another system running to finalize/post-process assets. This also will be easier when Bevy Assets V2 rolls out in 0.12 (iirc).

The only downside is that with my example you cant reuse existing asset loaders to load dependency assets. You can actually do this via LoadedAsset::with_dependency or LoadedAsset::add_dependency, simply by using 'load_context'.

let spell_handle: Handle<SpellAsset> =



On the plus side, with the first method you know that all dependency assets are loaded once the main asset is loaded, since the asset loader for the main asset has to load everything itself. If you used LoadedAsset::with_dependency etc, the main asset (in this example, Hero) may load before its SpellAsset loads, so you can end up having to handle the case where a Hero is loaded but its spells aren't.

Excited to see where this project goes!

undersquire
Автор

I've always saw macros as arcane magic. But it's looking usable now

RenderingUser
Автор

I kinda get the feeling you're trying to do OOP things inside Rust, specifically inside a framework that uses an ECS. What makes a hero a specific hero might be the spells that it has, it would make way more sense to construct that hero as an entity by adding the components it needs. The spell functionality should then go into an ECS system.

Then you don't need handles pointing to each other all over the place. Which is really un-Rusty.

stysner
Автор

Really good video, learned a lot from it! Confused about the symbols : ? at the end there, which you said were differentiators, can you pick any characters to separate parameters? Or do those have actual meaning to rust?

Ferret