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

时间:2019-05-04 13:40:45

标签: rust message-queue

我有一个图书馆(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中,最佳做法是什么?

2 个答案:

答案 0 :(得分:2)

我认为这里的问题是我们在内部使用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需要运行其内部循环来完成所有工作,但是很高兴能在另一个线程上工作。

检出examplesguide以获得更多信息

答案 1 :(得分:1)

Traceback (most recent call last): File "/data/user/0/ru.iiec.pydroid3/files/temp_iiec_codefile.py", line 19, in <module> buttonlist.append(Button(width=5, relief=SUNKEN, bg="Black", command=change_color(button_counter))) File "/data/user/0/ru.iiec.pydroid3/files/temp_iiec_codefile.py", line 11, in change_color buttonlist[button_number].configure(bg=color) IndexError: list index out of range 的API似乎非常严格。如果它在回调类型中使用了nannou特征,则可以使用闭包并捕获额外的参数。由于API是函数指针,因此您不得不使用全局状态来传递数据。

这是另一种方式。我假设您的数据和功能如下所示:

Fn*

也就是说,#[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 } 一样危险。我想其他功能至少不能以这种方式修改数据,但是您仍然必须小心避免并发错误。