Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
213 views
in Technique[技术] by (71.8m points)

rust - Is unsafe my only option to pass a reference to this method expecting a closure?

I'm using the cursive_table_view crate, and the table has a set_on_submit method to which you pass a closure to be executed when the user press over a selected row in the table. The signature of said method is:

 pub fn set_on_submit<F>(&mut self, cb: F)
    where
        F: Fn(&mut Cursive, usize, usize) + 'static,

Inside the closure, I'm trying to use something that actually comes as a parameter in the function that contains this code. I simply declare that parameter as &'static, and that's all good and expected. Now of course, I've just moved the problem to the caller of my function.

fn my_function(param: &'static MyType) {
   let table = TableView::new();
   table.set_on_sumbit(|cur, row, index| {
        do_something_with(param);
   }
}

Eventually I get to a point where I have to declare my variable as static to pass it as a parameter to that function, but because I need to modify this variable before the call (and after declaration), it needs to be static mutable. Now the problem is, when I try to modify the variable, the compiler says that I can only do that with unsafe.

I believe I understand the why of everything above. The closure is going to be called upon some event in the UI, so it makes sense that this lives for as long as the entire program.

I've tried several options with Rc and read a ton about lifetimes and even threads and messages, but I felt I was blindly trying things without really understanding at some point. Where I'm at now is that I believe I understand, but I'm uncomfortable with having to go the unsafe route suggestion.

Is it avoidable? Is my pattern wrong to begin with?

I didn't want to add more code to keep the explanation agnostic to everything else, but of course I'm happy to edit if more code helps understand the issue. Also, I don't really need to modify the struct any longer by the time I've passed the reference to the closure, in case that helps find a solution down that path.

question from:https://stackoverflow.com/questions/66056879/is-unsafe-my-only-option-to-pass-a-reference-to-this-method-expecting-a-closure

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Think of references as mostly (excluding &'static and a few other cases) being for temporary things, especially temporary things that live only as long as a stack frame. Therefore, don't try to use references for things like user interface callbacks.

Here's how to use std::rc::Rc instead, which ensures that the data lives as long as needed.

use std::rc::Rc;

fn my_function(param: Rc<MyType>) {        // changed parameter type
    let table = TableView::new();
    table.set_on_submit(|cur, row, index| {
        do_something_with(&param);         // & enables conversion to &MyType
    }
}

Now, in order to use this, you need to arrange so your data is owned by the Rc. It is not possible to have the above sort of callback and also have let data = MyType {...} directly, because the data must be owned by the Rc. (However, if useful, you can initialize it and then later put it in the Rc.)

fn main() {
    // Wrap it as soon as you construct it.
    // (Or at least before you start using callbacks.)
    let my_app_data = Rc::new(MyType {
        // whatever fields or constructor...
    });

    // Call the function. `.clone()` here makes a copy of the `Rc` refcounted pointer,
    // not the underlying data.
    my_function(my_app_data.clone());
}

If your program uses threads, then you must use the thread-safe reference counter std::sync::Arc instead of Rc.

If you want to modify the data while the callback exists — whether in a callback or elsewhere — you need to also use RefCell (or thread-safe std::sync::Mutex / RwLock) inside the Rc, which adds a few complications.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...