将值传递给参数之外的函数

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 需要 运行 它的内部循环来完成所有工作,但很高兴在另一个线程上。

查看 examples and guide 了解更多信息