4个版本
0.4.10 | 2023年12月15日 |
---|---|
0.4.7 | 2023年12月14日 |
0.4.6 | 2023年12月13日 |
0.4.5 | 2023年12月13日 |
#1007 in 过程宏
每月 24 次下载
在 ruwren 中使用
56KB
1.5K SLoC
Ruwren: Rust的Wren绑定

这是一个尝试以更Rust风格创建一些Rust Wren绑定的例子。它在某些时候表现得相当透明(你仍然需要处理槽位/外部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在原始外部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运行时将崩溃。
依赖项
约3.5MB
约64K SLoC