在 Rust 中创建工厂函数
Creating a factory function in Rust
我刚开始学习 Rust,遇到了一些障碍;
我正在尝试创建一个初始化 rusty_v8
库的函数。他们已经设置了以下代码:
use rusty_v8 as v8;
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap();
println!("javascript code: {}", code.to_rust_string_lossy(scope));
let script = v8::Script::compile(scope, code, None).unwrap();
let result = script.run(scope).unwrap();
let result = result.to_string(scope).unwrap();
println!("result: {}", result.to_rust_string_lossy(scope));
现在,我为自己设定了挑战,要让它可重复使用。我希望能够调用某种 init
函数,它 return 是一个 v8::Scope
对象,我可以用它来执行 v8::Script
对象。我已经成功地创建了这个函数:
pub(crate) fn init<'a>() -> v8::ContextScope<'a, v8::HandleScope<'a, v8::Context>> {
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let mut isolate = v8::Isolate::new(Default::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let context = v8::Context::new(&mut scope);
return v8::ContextScope::new(&mut scope, context);
}
到目前为止,我了解代码应该如何工作以及为什么不能工作。编译器说 return 语句:returns a value referencing data owned by the current function
。这对我来说很有意义,isolate
和 scope
变量是在此函数中创建的。但是如果我想把这个函数作为一个工厂,换句话说,让这个函数构造一个ContextScope
对象,isolate
和scope
对象必须保持存活。我将如何实现这一目标?
Rust 的默认规则不允许你编写你想写的函数。
每当你看到一个有生命周期的类型,比如HandleScope<'s>
,你应该理解这意味着这种类型的实例是临时的(生命周期的通常例外是“静态的,此处不适用”)并且通常存在于堆栈框架的范围内。为了return这样的类型,函数必须传递它借用的任何实例。
在这种情况下,库的意图是您应该遵循堆栈帧:HandleScope
的文档说“管理多个本地句柄的堆栈分配 class。”确切的措辞是无稽之谈——你可以自由地将 HandleScope
移动到 Box
中,从而使其成为堆分配的——但显然他们 期望 你使用它以面向堆栈的方式。
执行此操作的最简单方法是将函数修改为 accept 范围内的函数 运行:
pub(crate) fn init<F, R>(f: F) -> R
where
for<'a> F: FnOnce(v8::ContextScope<'a, v8::HandleScope<'a, v8::Context>>) -> R,
{
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let mut isolate = v8::Isolate::new(Default::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let context = v8::Context::new(&mut scope);
let cscope = v8::ContextScope::new(&mut scope, context);
f(cscope)
}
(注意:这似乎无法编译,因为您重叠借用了 scope
。我不熟悉 rusty_v8
,所以我不知道为什么他们的示例看起来像那样以及是否有错误。)
更复杂的方法是构造一个“自引用结构”,它可以包含所有需要的对象,同时它们相互引用。如果直接完成这是不安全的(因为可以移动结构)但是可以使用 ouroboros
crate 来管理它,它以内存安全的方式提供了必要的机制。这仍然强加了您必须从闭包中引用数据的约束,但是您可以重复而不是一次。
但是,您可能应该缩小工厂范围:坚持 returning a v8::Isolate
,并将其余部分作为“eval”而不是“init”过程。这似乎更接近图书馆的预期用途。
我刚开始学习 Rust,遇到了一些障碍;
我正在尝试创建一个初始化 rusty_v8
库的函数。他们已经设置了以下代码:
use rusty_v8 as v8;
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap();
println!("javascript code: {}", code.to_rust_string_lossy(scope));
let script = v8::Script::compile(scope, code, None).unwrap();
let result = script.run(scope).unwrap();
let result = result.to_string(scope).unwrap();
println!("result: {}", result.to_rust_string_lossy(scope));
现在,我为自己设定了挑战,要让它可重复使用。我希望能够调用某种 init
函数,它 return 是一个 v8::Scope
对象,我可以用它来执行 v8::Script
对象。我已经成功地创建了这个函数:
pub(crate) fn init<'a>() -> v8::ContextScope<'a, v8::HandleScope<'a, v8::Context>> {
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let mut isolate = v8::Isolate::new(Default::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let context = v8::Context::new(&mut scope);
return v8::ContextScope::new(&mut scope, context);
}
到目前为止,我了解代码应该如何工作以及为什么不能工作。编译器说 return 语句:returns a value referencing data owned by the current function
。这对我来说很有意义,isolate
和 scope
变量是在此函数中创建的。但是如果我想把这个函数作为一个工厂,换句话说,让这个函数构造一个ContextScope
对象,isolate
和scope
对象必须保持存活。我将如何实现这一目标?
Rust 的默认规则不允许你编写你想写的函数。
每当你看到一个有生命周期的类型,比如HandleScope<'s>
,你应该理解这意味着这种类型的实例是临时的(生命周期的通常例外是“静态的,此处不适用”)并且通常存在于堆栈框架的范围内。为了return这样的类型,函数必须传递它借用的任何实例。
在这种情况下,库的意图是您应该遵循堆栈帧:HandleScope
的文档说“管理多个本地句柄的堆栈分配 class。”确切的措辞是无稽之谈——你可以自由地将 HandleScope
移动到 Box
中,从而使其成为堆分配的——但显然他们 期望 你使用它以面向堆栈的方式。
执行此操作的最简单方法是将函数修改为 accept 范围内的函数 运行:
pub(crate) fn init<F, R>(f: F) -> R
where
for<'a> F: FnOnce(v8::ContextScope<'a, v8::HandleScope<'a, v8::Context>>) -> R,
{
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let mut isolate = v8::Isolate::new(Default::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let context = v8::Context::new(&mut scope);
let cscope = v8::ContextScope::new(&mut scope, context);
f(cscope)
}
(注意:这似乎无法编译,因为您重叠借用了 scope
。我不熟悉 rusty_v8
,所以我不知道为什么他们的示例看起来像那样以及是否有错误。)
更复杂的方法是构造一个“自引用结构”,它可以包含所有需要的对象,同时它们相互引用。如果直接完成这是不安全的(因为可以移动结构)但是可以使用 ouroboros
crate 来管理它,它以内存安全的方式提供了必要的机制。这仍然强加了您必须从闭包中引用数据的约束,但是您可以重复而不是一次。
但是,您可能应该缩小工厂范围:坚持 returning a v8::Isolate
,并将其余部分作为“eval”而不是“init”过程。这似乎更接近图书馆的预期用途。