将值传递给参数之外的函数
Passing value to function outside of arguments
我得到了一个图书馆 (nannou) wants to call a function 签名 fn(&'r nannou::App, nannou::Frame) -> nannou::Frame
。
我需要向这个函数(我的图像缓冲区)传递一些额外的值。
我的应用程序如下所示:
fn main {
let buff = Buff::generate(..);
nannou::view(view);
}
fn view(app: &App, frame: Frame) -> Frame {...}
我需要将 buff
传递给 view
。我尝试使用 partial_application,但 Rust 抱怨说 expected fn pointer, found closure
.
我该怎么做?一,我知道的错误和丑陋的方式 - 使用全局变量。
有没有更好的方法? Rust 中的最佳实践是什么?
似乎 nannou
的 API 非常严格。如果它在回调类型中使用 Fn*
特征,您可以使用闭包并捕获您的额外参数。由于 API 是一个函数指针,您不得不使用全局状态来传递数据。
这是另一种方法。我假设您的数据和函数如下所示:
#[derive(Debug)]
struct ExtraData {
data: usize,
}
type MyViewFn = fn(app: &nannou::App, frame: nannou::Frame, extra: &mut ExtraData) -> nannou::Frame;
fn my_callback(app: &nannou::App, frame: nannou::Frame, extra: &mut ExtraData) -> nannou::Frame {
println!("{:?}", extra);
frame
}
fn main() {
call_view_with(my_callback, ExtraData { data: 42 });
}
即call_view_with
包裹view
以获取额外的参数。它的工作原理是:
// This function is unsafe and should not be called concurrently to avoid difficult bugs
fn call_view_with(callback: MyViewFn, extra: ExtraData) {
// static mut needs to be initialized with a constant expression for some reason
static mut static_extra: ExtraData = ExtraData::default();
// Using mutable static requires unsafe
unsafe {
static_extra.data = extra.data;
}
static mut static_func_ptr: MyViewFn = default_callback;
unsafe {
static_func_ptr = callback;
}
// Rust allows nested function definitions. They can not capture dynamic local variables,
// only const and static variables.
fn view_fn(app: &nannou::App, frame: nannou::Frame) -> nannou::Frame {
unsafe { return static_func_ptr(app, frame, &mut static_extra) }
}
nannou::view(view_fn);
}
impl ExtraData {
const fn default() -> Self {
ExtraData { data: 0 }
}
}
fn default_callback(
app: &nannou::App,
frame: nannou::Frame,
extra: &mut ExtraData,
) -> nannou::Frame {
frame
}
如评论中所述,它的危险性不亚于全局定义 static mut
。我想至少其他函数不能以这种方式修改数据,但您仍然必须小心避免并发错误。
我认为这里的问题是我们使用 view(..)
函数作为内部回调来绘制图形。因此,最低限度的设置如下所示:
fn main() {
nannou::sketch(view);
}
fn view(app: &App, frame: Frame) -> Frame {
// Draw stuff
}
无论你想传递数据,我们都需要像这样使用 Model
:
fn main() {
nannou::app(model).update(update).run();
}
struct Model {
my_data: MyData,
}
fn model(app: &App) -> Model {
app
.new_window()
.with_dimensions(720, 720)
.view(view)
.build()
.unwrap();
let my_data = MyData::new();
Model { my_data }
}
fn update(_app: &App, _model: &mut Model, _update: Update) {}
fn view(app: &App, model: &Model, frame: Frame) -> Frame {
// Draw stuff
}
注意视图函数在这样设置时有不同的签名。它包括一个 Model
,您可以将自己的数据放入其中。当您想在 update()
函数中更新它时,它是不可变的,但如果需要,您可以使用 RefCell
来解决这个问题。
我通常做的是从 model()
函数启动我的其他线程,然后使用 Model
中的通道与 nannou 循环通信,例如:
fn model(app: &App) -> Model {
let (talk_to_nannou, data_from_my_thread) = mpsc::channel();
thread::spawn(|| {
//All the stuff I want to do
talk_to_nannou.send("Hey");
});
Model {
data_from_my_thread,
};
}
fn view(app: &App, model: &Model, frame: Frame) -> Frame {
if let Some(msg) = model.data_from_my_thread.try_recv() {
dbg!(msg);
}
}
如果像这样将它添加到现有应用程序中,您可以用不同的方式来考虑它:
fn main() {
// My awesome app that has heaps of cool stuff
thread::spawn(|| {
nannou::app(model).update(update).run();
});
// Do more stuff in my cool app
}
struct Model {
my_data: MyData,
}
fn model(app: &App) -> Model {
app.new_window()
.with_dimensions(720, 720)
.view(view)
.build()
.unwrap();
let my_data = MyData::new();
Model { my_data }
}
fn update(_app: &App, _model: &mut Model, _update: Update) {}
fn view(app: &App, model: &Model, frame: Frame) -> Frame {
// Draw stuff
}
然后你可以将所有 nannou 的东西塞进一个模块中,但这有点取决于你想如何安排。唯一的问题是 nannou 需要 运行 它的内部循环来完成所有工作,但很高兴在另一个线程上。
我得到了一个图书馆 (nannou) wants to call a function 签名 fn(&'r nannou::App, nannou::Frame) -> nannou::Frame
。
我需要向这个函数(我的图像缓冲区)传递一些额外的值。
我的应用程序如下所示:
fn main {
let buff = Buff::generate(..);
nannou::view(view);
}
fn view(app: &App, frame: Frame) -> Frame {...}
我需要将 buff
传递给 view
。我尝试使用 partial_application,但 Rust 抱怨说 expected fn pointer, found closure
.
我该怎么做?一,我知道的错误和丑陋的方式 - 使用全局变量。
有没有更好的方法? Rust 中的最佳实践是什么?
似乎 nannou
的 API 非常严格。如果它在回调类型中使用 Fn*
特征,您可以使用闭包并捕获您的额外参数。由于 API 是一个函数指针,您不得不使用全局状态来传递数据。
这是另一种方法。我假设您的数据和函数如下所示:
#[derive(Debug)]
struct ExtraData {
data: usize,
}
type MyViewFn = fn(app: &nannou::App, frame: nannou::Frame, extra: &mut ExtraData) -> nannou::Frame;
fn my_callback(app: &nannou::App, frame: nannou::Frame, extra: &mut ExtraData) -> nannou::Frame {
println!("{:?}", extra);
frame
}
fn main() {
call_view_with(my_callback, ExtraData { data: 42 });
}
即call_view_with
包裹view
以获取额外的参数。它的工作原理是:
// This function is unsafe and should not be called concurrently to avoid difficult bugs
fn call_view_with(callback: MyViewFn, extra: ExtraData) {
// static mut needs to be initialized with a constant expression for some reason
static mut static_extra: ExtraData = ExtraData::default();
// Using mutable static requires unsafe
unsafe {
static_extra.data = extra.data;
}
static mut static_func_ptr: MyViewFn = default_callback;
unsafe {
static_func_ptr = callback;
}
// Rust allows nested function definitions. They can not capture dynamic local variables,
// only const and static variables.
fn view_fn(app: &nannou::App, frame: nannou::Frame) -> nannou::Frame {
unsafe { return static_func_ptr(app, frame, &mut static_extra) }
}
nannou::view(view_fn);
}
impl ExtraData {
const fn default() -> Self {
ExtraData { data: 0 }
}
}
fn default_callback(
app: &nannou::App,
frame: nannou::Frame,
extra: &mut ExtraData,
) -> nannou::Frame {
frame
}
如评论中所述,它的危险性不亚于全局定义 static mut
。我想至少其他函数不能以这种方式修改数据,但您仍然必须小心避免并发错误。
我认为这里的问题是我们使用 view(..)
函数作为内部回调来绘制图形。因此,最低限度的设置如下所示:
fn main() {
nannou::sketch(view);
}
fn view(app: &App, frame: Frame) -> Frame {
// Draw stuff
}
无论你想传递数据,我们都需要像这样使用 Model
:
fn main() {
nannou::app(model).update(update).run();
}
struct Model {
my_data: MyData,
}
fn model(app: &App) -> Model {
app
.new_window()
.with_dimensions(720, 720)
.view(view)
.build()
.unwrap();
let my_data = MyData::new();
Model { my_data }
}
fn update(_app: &App, _model: &mut Model, _update: Update) {}
fn view(app: &App, model: &Model, frame: Frame) -> Frame {
// Draw stuff
}
注意视图函数在这样设置时有不同的签名。它包括一个 Model
,您可以将自己的数据放入其中。当您想在 update()
函数中更新它时,它是不可变的,但如果需要,您可以使用 RefCell
来解决这个问题。
我通常做的是从 model()
函数启动我的其他线程,然后使用 Model
中的通道与 nannou 循环通信,例如:
fn model(app: &App) -> Model {
let (talk_to_nannou, data_from_my_thread) = mpsc::channel();
thread::spawn(|| {
//All the stuff I want to do
talk_to_nannou.send("Hey");
});
Model {
data_from_my_thread,
};
}
fn view(app: &App, model: &Model, frame: Frame) -> Frame {
if let Some(msg) = model.data_from_my_thread.try_recv() {
dbg!(msg);
}
}
如果像这样将它添加到现有应用程序中,您可以用不同的方式来考虑它:
fn main() {
// My awesome app that has heaps of cool stuff
thread::spawn(|| {
nannou::app(model).update(update).run();
});
// Do more stuff in my cool app
}
struct Model {
my_data: MyData,
}
fn model(app: &App) -> Model {
app.new_window()
.with_dimensions(720, 720)
.view(view)
.build()
.unwrap();
let my_data = MyData::new();
Model { my_data }
}
fn update(_app: &App, _model: &mut Model, _update: Update) {}
fn view(app: &App, model: &Model, frame: Frame) -> Frame {
// Draw stuff
}
然后你可以将所有 nannou 的东西塞进一个模块中,但这有点取决于你想如何安排。唯一的问题是 nannou 需要 运行 它的内部循环来完成所有工作,但很高兴在另一个线程上。