33个版本
0.4.10 | 2023年12月15日 |
---|---|
0.4.4 | 2023年9月3日 |
0.4.2 | 2021年12月31日 |
0.4.1 |
|
0.3.1 | 2020年6月7日 |
#46 在 编程语言
97 每月下载量
用于 game_kernel
405KB
10K SLoC
Ruwren: Wren绑定用于Rust

这是一个在更Rust风格的尝试,制作一些Rust Wren绑定。它在某些时候表现得相当透明(你仍然需要处理slot / foreign API),但它应该是
- 比直接使用C API更类型安全
- 不应该妨碍快速执行
- 应该相对简单易上手
包括
只需将
ruwren = "0.4"
添加到你的Cargo.toml。
主要API
创建一个虚拟机
要创建一个虚拟机,使用VMConfig结构体。要创建一个将输出记录到控制台的基本虚拟机,只需调用
let vm = VMConfig::new().build();
你可以通过interpret直接运行代码
vm.interpret("main", r##"System.print("Cool beans!")"##).unwrap();
在成功执行时返回一个 Ok(())
,在失败时返回 Err(e)
(有关更多详细信息,请参阅VMError)。
你还可以使用FunctionHandle调用Wren中定义的代码,如下所示
vm.interpret("main", r##"
class GameEngine {
static update(delta) {
System.print(delta)
}
}
"##).unwrap();
let handle = vm.make_call_handle(FunctionSignature::new_function("update", 1));
vm.execute(|vm| {
vm.ensure_slots(2);
vm.get_variable("main", "GameEngine", 0);
vm.set_slot_double(1, 0.016);
});
vm.call_handle(&handle);
或更直接地
vm.interpret("main", r##"
class GameEngine {
static update(delta) {
System.print(delta)
}
}
"##).unwrap();
vm.execute(|vm| {
vm.ensure_slots(2);
vm.get_variable("main", "GameEngine", 0);
vm.set_slot_double(1, 0.016);
});
vm.call(FunctionSignature::new_function("update", 1));
在Wren中嵌入Rust代码
以下是一个简短的示例,说明如何将你的Rust数据嵌入到Wren中
use ruwren::{Class, VM, VMConfig, ModuleLibrary, get_slot_checked, create_module};
struct Foo {
bar: f64,
}
impl Class for Foo {
fn initialize(vm: &VM) -> Self {
let bar = get_slot_checked!(vm => num 1);
Foo { bar }
}
}
impl Foo {
fn instance(&self, vm: &VM) {
vm.set_slot_double(0, self.bar);
}
fn static_fn(vm: &VM) {
let num = get_slot_checked!(vm => num 1);
vm.set_slot_double(0, num + 5.0)
}
}
create_module! {
class("Foo") crate::Foo => foo {
instance(fn "instance", 0) instance,
static(fn "static_fn", 1) static_fn
}
module => foobar
}
fn main() {
let mut lib = ModuleLibrary::new();
foobar::publish_module(&mut lib);
let vm = VMConfig::new().library(&lib).build();
vm.interpret("foobar", r##"
foreign class Foo {
construct new(bar) {}
foreign instance()
foreign static static_fn(num)
}
"##).unwrap();
// You could now write Wren code like:
vm.interpret("main", r##"
import "foobar" for Foo
var f = Foo.new(4)
System.print(Foo.static_fn(f.instance()))
"##).unwrap();
// This should print "9".
}
V2 Foreign
V2 foreigns在原始foreign API之上模拟Wren的类系统,因此上述示例将是
use ruwren::{wren_impl, wren_module, ModuleLibrary, VMConfig, WrenObject};
#[derive(WrenObject, Default)]
struct Foo {
bar: f64,
}
#[wren_impl]
impl Foo {
/*
you can also write out an allocator, if you
don't want the base struct itself to implement Default
#[wren_impl(allocator)]
fn allocator() -> FooClass {
FooClass {}
}
*/
#[wren_impl(constructor)]
fn constructor(&self, bar: f64) -> FooInstance {
FooInstance { bar }
}
#[wren_impl(instance)]
fn instance(&self) -> f64 {
self.bar
}
fn static_fn(&self, num: f64) -> f64 {
num + 5.0
}
}
wren_module! {
mod foobar {
pub crate::Foo;
}
}
fn main() {
let mut lib = ModuleLibrary::new();
foobar::publish_module(&mut lib);
let vm = VMConfig::new().library(&lib).build();
vm.interpret("foobar", r##"
foreign class Foo {
construct new(bar) {}
foreign instance()
foreign static static_fn(num)
}
"##).unwrap();
// You could now write Wren code like:
vm.interpret("main", r##"
import "foobar" for Foo
var f = Foo.new(4)
System.print(Foo.static_fn(f.instance()))
"##).unwrap();
// This should print "9".
}
关于WASM编译
只要你的目标WASI,并且你在某处设置了一个WASI SDK,它在技术上就可以工作。请查看justfile或example-wasi.nu,以获取运行所需设置的环
境变量。但是有一个大前提
WASM(即使是WASI)是一个panic=abort平台,因此catch_unwind不起作用,panic是无法处理的。
这意味着v1外部API(例如 get_slot_checked!
)的一些惯用语在该平台上表现不佳。基本上,任何引发恐慌的操作都无法正常工作。
对v2外部API进行最小更改(即构造函数可能失败)意味着v2在Web上应该相对不变,v1是 可用的,只是不应该触发恐慌,否则wasm运行时会崩溃。
依赖项
~0.3–3MB
~63K SLoC