简体   繁体   English

有没有办法(以某种方式)在 rust 中创建一个(Box of)闭包的拥有版本?

[英]Is there no way to (one way or another) create an owned version of a (Box of) closure in rust?

I want to write a FuncWrapper struct with a new function that takes a (Boxed) closure as parameter, and returns a decorated closure that just adds some boilerplate to the incoming one.我想用一个new的 function 编写一个FuncWrapper结构,它将一个 (Boxed) 闭包作为参数,并返回一个装饰闭包,它只是向传入的闭包添加一些样板。 But I also want the returned value to be "owned", to allow for the following (eg):但我也希望返回值被“拥有”,以允许以下(例如):

fn main() {                                                                                                                                                                                                                           
                                                                                                                                                                                                                                      
    let a: FuncWrapper<u32>;                                                                                                                                                                                                          
                                                                                                                                                                                                                                      
    {                                                                                                                                                                                                                                 
        let foo = |x: u32| {print!("{}", x)};                                                                                                                                                                                         
        let b = &Box::new(foo);                                                                                                                                                                                                       
        a = FuncWrapper::new(b);                                                                                                                                                                                                     
    }                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                      
    let _c = (a.func)(42);                                                                                                                                                                                                            
}                                                                                                                                                                                                                                          

That is, I want for the return value of new to be an "owned" value.也就是说,我希望 new 的返回值是一个“拥有的”值。

Now I recently learned that all closures in Rust (being Trais) must have a lifetime associated with them (and it will default to 'static if not specified.) But I suppose its seems that any lifetime for the closure in the FuncWrapper of the return value of new will be wrong.现在我最近了解到 Rust 中的所有闭包(作为 Trais)必须具有与之相关联的生命周期(如果未指定,它将默认为'static 。)但我想它似乎是返回的 FuncWrapper 中闭包的任何生命周期new的价值将是错误的。

I don't want the lifetime to be that of the incoming reference, because this will be too short.我不希望生命周期是传入引用的生命周期,因为这太短了。 Eg the following will not work because it the lifetime of the return value is that of the parameter f_box_ref which does not live long enough in the case I provide above例如,以下将不起作用,因为返回值的生命周期是参数f_box_ref的生命周期,在我上面提供的情况下,它的生命周期不够长

struct FuncWrapper<'b, DomainType> {                                                                                                                                                                                                  
    func: Box<dyn Fn(DomainType) + 'b>                                                                                                                                                                                                
}                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                      
impl<'b, DomainType> FuncWrapper<'b, DomainType> {                                                                                                                                                                                    
    fn new<F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<'b, DomainType> {                                                                                                                                       
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (**f_box_ref)(a)                                                                                                                                                                                                          
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}    

results in结果是

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:22:18
   |
22 |         let b = &Box::new(foo);
   |                  ^^^^^^^^^^^^^ creates a temporary which is freed while still in use
23 |         a = FuncWrapper::new(b);
24 |     }
   |     - temporary value is freed at the end of this statement
...
27 | }
   | - borrow might be used here, when `a` is dropped and runs the destructor for type `FuncWrapper<'_, u32>`
   |
   = note: consider using a `let` binding to create a longer lived value

Changing the lifetime of the return parameter to 'static also seems wrong, since my goal is to create a new closure inside the new function` , (so how could it be static?) but regardless this:将返回参数的生命周期更改为'static似乎也是错误的,因为我的目标是new函数中创建一个新的闭包`,(所以它怎么可能是 static?)但无论如何:

struct FuncWrapper<DomainType> {                                                                                                                                                                                                      
    func: Box<dyn Fn(DomainType) + 'static>                                                                                                                                                                                           
}                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                      
impl<DomainType> FuncWrapper<DomainType> {                                                                                                                                                                                            
    fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {                                                                                                                                       
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (**f_box_ref)(a)                                                                                                                                                                                                          
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}                                                                                                                                                                                                                                     

errors with错误与

error[E0759]: `f_box_ref` has lifetime `'b` but it needs to satisfy a `'static` lifetime requirement
  --> src/main.rs:7:21
   |
6  |       fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {
   |                                                          ---------- this data with lifetime `'b`...
7  |           let new_f = move |a: DomainType| {
   |  _____________________^
8  | |             // ..add some boilerplate then
9  | |             (**f_box_ref)(a)
10 | |         };
   | |_________^ ...is used here...
11 |           let b = Box::new(new_f);
12 |           FuncWrapper {func: b}
   |                              - ...and is required to live as long as `'static` here

The error surprised me since I thought the job of move was to capture by value.这个错误让我感到惊讶,因为我认为move的工作是按价值捕获。 At first I thought that I need to dereference the f_box_ref first?起初我以为我需要先取消引用f_box_ref But that但是那个

impl<DomainType> FuncWrapper<DomainType> {                                                                                                                                                                                            
    fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {                                                                                                                                       
        let f_box: Box<F> =  *f_box_ref;                                                                                                                                                                                              
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (*f_box)(a)                                                                                                                                                                                                               
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}                                                                                                                                                                                                                                     
           

results in结果是

error[E0507]: cannot move out of `*f_box_ref` which is behind a shared reference
 --> src/main.rs:7:30
  |
7 |         let f_box: Box<F> =  *f_box_ref;
  |                              ^^^^^^^^^^
  |                              |
  |                              move occurs because `*f_box_ref` has type `Box<F>`, which does not implement the `Copy` trait
  |                              help: consider borrowing here: `&*f_box_ref`

Alas, following the hint to "help: consider borrowing here: &*f_box_ref " doesn't help either las,按照“帮助:考虑在这里借用: &*f_box_ref ”的提示也无济于事

impl<DomainType> FuncWrapper<DomainType> {                                                                                                                                                                                            
    fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {                                                                                                                                       
        let f_box: &Box<F> =  &*f_box_ref;                                                                                                                                                                                            
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (*f_box)(a)                                                                                                                                                                                                               
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}

errors with错误与

error[E0759]: `f_box_ref` has lifetime `'b` but it needs to satisfy a `'static` lifetime requirement
  --> src/main.rs:7:31
   |
6  |     fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {
   |                                                        ---------- this data with lifetime `'b`...
7  |         let f_box: &Box<F> =  &*f_box_ref;
   |                               ^^^^^^^^^^^ ...is used here...
...
13 |         FuncWrapper {func: b}
   |                            - ...and is required to live as long as `'static` here

so obviously we're still not returning anything like a new owned closure, and any choice for the lifetime of the returned closure seems arbitrary and wrong.所以很明显我们仍然没有返回任何类似新拥有的闭包的东西,并且对返回的闭包的生命周期的任何选择似乎都是武断和错误的。

What I'm trying to do here seems like it should be common.我在这里尝试做的事情似乎应该很普遍。 What am I missing?我错过了什么?

It's fairly straight forward, to get an owned type from a reference you have 3 options that come to mind immediately: Copy , Clone or ToOwned .这非常简单,要从引用中获取拥有的类型,您会立即想到 3 个选项: CopyCloneToOwned ToOwned is a little more advanced since it can change the type as well and since Copy requires Cloned I'll focus on the later. ToOwned稍微高级一点,因为它也可以更改类型,并且由于Copy需要Cloned ,我将重点关注后者。

To get an owned Fn from a &Box<Fn> you can just call clone() :要从&Box<Fn>获取拥有的Fn ,您只需调用clone()

impl<DomainType> FuncWrapper<DomainType> {
    fn new<F: Fn(DomainType) + Clone + 'static>(f_box_ref: &Box<F>) -> FuncWrapper<DomainType> {
        let f_box = f_box_ref.clone();
        let new_f = move |a: DomainType| {
            // ..add some boilerplate then
            (f_box)(a)
        };
        let b = Box::new(new_f);
        FuncWrapper { func: b }
    }
}

Since it is not FnMut , just Fn maybe you can use an Rc instead of Box ?因为它不是FnMut ,只是Fn也许你可以使用Rc而不是Box You can always clone an Rc你总是可以克隆一个Rc

use std::rc::Rc;

impl<DomainType> FuncWrapper<DomainType> {
    fn new<F: Fn(DomainType) + 'static>(f_rc: Rc<F>) -> FuncWrapper<DomainType> {
        let new_f = move |a: DomainType| {
            // ..add some boilerplate then
            (f_rc)(a)
        };
        let b = Box::new(new_f);
        FuncWrapper { func: b }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 有没有办法注释 function 在 rust 中采用可选闭包? - Is there a way to annotate a function that takes an optional closure in rust? rust 中函数接受闭包作为参数或返回闭包的惯用方式是什么? - What is the idiomatic way in rust for a function accepts a closure as argument or return a closure? 有没有办法将此闭包更改为异步闭包? - Is there a way to change this closure to an async one? 有没有办法让 Rust 闭包只将一些变量移入其中? - Is there a way to have a Rust closure that moves only some variables into it? 从Rust中的元组内的自有盒子借用 - Borrowing from an owned box inside a tuple in Rust 用盒子封闭<t> arguments 在 Rust</t> - Closure with Box<T> arguments in Rust 这是在rust中的线程之间共享闭包回调的惯用方式吗? - Is this the idiomatic way to share a closure callback among threads in rust? Rust 从闭包内部继续使用的方式是什么? - What is the Rust way of using continue from inside a closure? 如何使用 Rust 和 wasm-bindgen 创建一个闭包,该闭包使用 state 创建另一个闭包? - How can I use Rust with wasm-bindgen to create a closure that creates another closure with state? Rust是否包含一种直接检查一个向量是否是另一个向量的“子串”的方法? - Does Rust contain a way to directly check whether or not one vector is a “substring” of another?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM