三年全职 Rust 游戏开发,真要放弃 Rust 吗?(5)
“过程宏不是反射”
游戏开发领域需要很多“动态方法”,Rust 没有这种“脚本语言”能力。尤其是在关卡编辑、工具和调试方面,这变得尤为痛苦。
“其实,C/Cpp 也没有啊,所以结合 lua 之类的脚本语言比较常见。我一直在想,社区也许需要一门和 Rust 语法相同且和 Rust 交互的脚本语言。
Rust 中有过程宏。但是作者认为过程宏基本上允许程序员在编译时运行代码,消耗 Rust 的 AST,并生成新的代码。不幸的是,这种方法存在许多问题。
“首先,过程宏并没有真正缓存,而是在重新编译时重新运行。这导致你的代码必须分割成多个 crate,这并不总是可行的,而且如果你更加依赖过程宏,编译时间会大大增加。有很多方便的过程宏,比如
profiling的function宏,非常有用,但最终无法使用,因为它们破坏了增量构建时间。其次,过程宏非常难以编写,大多数人最终使用非常庞大的辅助 crate,比如syn,它是一个非常庞大的 Rust 解析器,会急切地评估应用于它的所有内容。例如,如果你想在宏中注释一个函数并解析它的名称,syn最终会解析整个函数体。还有一种情况,syn的作者也是serde的作者,这是一个流行的 Rust 序列化库。去年的某个时候,该库在一个补丁版本中开始附带一个二进制文件,拒绝了社区的反对声音。这并不是反对 Rust 的案例,但我觉得应该提到,因为它展示了生态系统的很大一部分是由单个开发者制作的库构建的,这些开发者可能会做出潜在危险的决策。当然,这种情况在任何语言中都可能发生,但在过程宏方面尤为重要,因为生态系统中几乎所有的东西都使用了这个特定作者的库(syn,serde,anyhow,thiserror,quote,...)。即使忽略上述情况,过程宏的学习曲线非常陡峭,并且它们必须在一个单独的 crate 中定义。这意味着与声明式宏不同,你不能轻松地创建一个新的过程宏,就像创建一个函数一样。相比之下,在 C# 中使用反射非常容易,如果性能不是问题(在使用反射的情况下通常不是问题),它可以是构建工具或调试的一种非常快速和有用的选项。Rust 并没有提供类似的功能,而在去年的 Rust 事件(ThePhd Keynote 事件)中,最后一种编译时反射[10]的方法基本上被取消了。
作者认为 Rust 缺乏像其他语言那样运行时真正的反射,是个缺陷。
不可否认,这确实是 Rust 中的缺陷。否则,Bevy 引擎也不会自己去实现 bevy_reflection 库来解决这个问题。然而,文章作者没有提及 Bevy 这个反射库。
从 Bevy 的资料来看,内置 Reflect trait 实现了序列化、反序列化和动态属性访问。实际上也是基于 Rust Any trait 实现的。
代码语言:javascript
复制
// Deriving `Reflect` implements the relevant reflection traits. In this case, it implements the
// `Reflect` trait and the `Struct` trait `derive(Reflect)` assumes that all fields also implement
// Reflect.
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Component, Reflect, Default)]
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
struct ComponentA {
pub x: f32,
pub y: f32,
}
这样就能够动态访问字段:
代码语言:javascript
复制
fn some_system() {
let mut value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar { b: 8 },
};
// You can set field values like this. The type must match exactly or this will fail.
*value.get_field_mut("a").unwrap() = 2usize;
assert_eq!(value.a, 2);
assert_eq!(*value.get_field::<usize>("a").unwrap(), 2);
// You can also get the &dyn Reflect value of a field like this
let field = value.field("a").unwrap();
// you can downcast Reflect values like this:
assert_eq!(*field.downcast_ref::<usize>().unwrap(), 2);
}
bevy_reflection 也是经历了好几个版本的迭代改进。Bevy 做的好的一面就是其社区维护的不错,每年都会进行反思,并且举办 bevy 游戏 jam 比赛,其实一个游戏引擎的发展也离不开用户的反馈。
也许是因为 Bevy 社区确实做的很好,所以大家才夸 Bevy,Bevy 在 Rust 社区才有一定影响力,甚至 Rust 编译器代码中都包含了为 Bevy 而特别编写的代码。
但是介于作者并不认可 Bevy ECS Everything 的理念,所以我想他也不会去用 bevy_reflection 库。
如有侵权请通知删除,谢谢!
本文转自;三年全职 Rust 游戏开发,真要放弃 Rust 吗?-腾讯云开发者社区-腾讯云 (tencent.com)